From e4ec81cce515574580b3cdc3e3a4b0b718dc417f Mon Sep 17 00:00:00 2001 From: Javier Campos Date: Tue, 17 Dec 2024 12:02:00 -0600 Subject: [PATCH 1/8] added check for conv implementation --- hls4ml/backends/vivado/passes/conv_stream.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hls4ml/backends/vivado/passes/conv_stream.py b/hls4ml/backends/vivado/passes/conv_stream.py index e0bb853d83..1c5f311a61 100644 --- a/hls4ml/backends/vivado/passes/conv_stream.py +++ b/hls4ml/backends/vivado/passes/conv_stream.py @@ -18,7 +18,7 @@ def transform(self, model, node): raise Exception(f'Cannot generate instructions for node {node.name} ({node_class})') def _generate_1d_instructions(self, node): - if node.model.config.get_config_value('IOType') == 'io_stream': + if node.model.config.get_config_value('IOType') == 'io_stream' and node.get_attr('conv_implementation') == 'Encoded': min_w, instructions = node.model.config.backend.compute_conv1d_instructions( node.get_input_variable().shape[0], node.get_input_variable().shape[1], @@ -34,7 +34,7 @@ def _generate_1d_instructions(self, node): node.set_attr('instructions', '0') def _generate_2d_instructions(self, node): - if node.model.config.get_config_value('IOType') == 'io_stream': + if node.model.config.get_config_value('IOType') == 'io_stream' and node.get_attr('conv_implementation') == 'Encoded': min_h, min_w, instructions = node.model.config.backend.compute_conv2d_instructions( node.get_input_variable().shape[0], node.get_input_variable().shape[1], From f9472766fd3963b75c703fde70a386883d3f1eae Mon Sep 17 00:00:00 2001 From: Javier Campos Date: Wed, 18 Dec 2024 13:01:09 -0600 Subject: [PATCH 2/8] Updated node attr key and made case insensitive --- hls4ml/backends/vivado/passes/conv_stream.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hls4ml/backends/vivado/passes/conv_stream.py b/hls4ml/backends/vivado/passes/conv_stream.py index 1c5f311a61..bae0f4a080 100644 --- a/hls4ml/backends/vivado/passes/conv_stream.py +++ b/hls4ml/backends/vivado/passes/conv_stream.py @@ -18,7 +18,7 @@ def transform(self, model, node): raise Exception(f'Cannot generate instructions for node {node.name} ({node_class})') def _generate_1d_instructions(self, node): - if node.model.config.get_config_value('IOType') == 'io_stream' and node.get_attr('conv_implementation') == 'Encoded': + if node.model.config.get_config_value('IOType') == 'io_stream' and node.get_attr('implementation').casefold() == 'encoded': min_w, instructions = node.model.config.backend.compute_conv1d_instructions( node.get_input_variable().shape[0], node.get_input_variable().shape[1], @@ -34,7 +34,7 @@ def _generate_1d_instructions(self, node): node.set_attr('instructions', '0') def _generate_2d_instructions(self, node): - if node.model.config.get_config_value('IOType') == 'io_stream' and node.get_attr('conv_implementation') == 'Encoded': + if node.model.config.get_config_value('IOType') == 'io_stream' and node.get_attr('implementation').casefold() == 'encoded': min_h, min_w, instructions = node.model.config.backend.compute_conv2d_instructions( node.get_input_variable().shape[0], node.get_input_variable().shape[1], From 6742c01196c177742b330a2a4ed0835ca28464a1 Mon Sep 17 00:00:00 2001 From: Javier Campos Date: Wed, 18 Dec 2024 15:34:58 -0600 Subject: [PATCH 3/8] Updated Conv1D and Conv2d optimizer match and default linebuffer values --- hls4ml/backends/vivado/passes/conv_stream.py | 57 +++++++++---------- .../vivado/passes/convolution_templates.py | 11 ++++ 2 files changed, 37 insertions(+), 31 deletions(-) diff --git a/hls4ml/backends/vivado/passes/conv_stream.py b/hls4ml/backends/vivado/passes/conv_stream.py index bae0f4a080..d7f6dca8dc 100644 --- a/hls4ml/backends/vivado/passes/conv_stream.py +++ b/hls4ml/backends/vivado/passes/conv_stream.py @@ -6,7 +6,12 @@ class GenerateConvStreamingInstructions(OptimizerPass): '''Generates the instructions for streaming implementation of CNNs''' def match(self, node): - return isinstance(node, (Conv1D, SeparableConv1D, Conv2D, SeparableConv2D)) + is_match = ( + isinstance(node, (Conv1D, SeparableConv1D, Conv2D, SeparableConv2D)) + and node.model.config.get_config_value('IOType').casefold() == 'io_stream' + and node.get_attr('implementation').casefold() == 'encoded' + ) + return is_match def transform(self, model, node): node_class = node.__class__.__name__ @@ -18,35 +23,25 @@ def transform(self, model, node): raise Exception(f'Cannot generate instructions for node {node.name} ({node_class})') def _generate_1d_instructions(self, node): - if node.model.config.get_config_value('IOType') == 'io_stream' and node.get_attr('implementation').casefold() == 'encoded': - min_w, instructions = node.model.config.backend.compute_conv1d_instructions( - node.get_input_variable().shape[0], - node.get_input_variable().shape[1], - node.get_attr('filt_width'), - node.get_attr('stride_width'), - ) - instructions_str = ','.join(str(i) for i in instructions) - node.set_attr('min_width', min_w) - node.set_attr('instructions', instructions_str) - else: - # these are unused; just put dummy values - node.set_attr('min_width', node.get_attr('in_width')) - node.set_attr('instructions', '0') + min_w, instructions = node.model.config.backend.compute_conv1d_instructions( + node.get_input_variable().shape[0], + node.get_input_variable().shape[1], + node.get_attr('filt_width'), + node.get_attr('stride_width'), + ) + instructions_str = ','.join(str(i) for i in instructions) + node.set_attr('min_width', min_w) + node.set_attr('instructions', instructions_str) def _generate_2d_instructions(self, node): - if node.model.config.get_config_value('IOType') == 'io_stream' and node.get_attr('implementation').casefold() == 'encoded': - min_h, min_w, instructions = node.model.config.backend.compute_conv2d_instructions( - node.get_input_variable().shape[0], - node.get_input_variable().shape[1], - node.get_input_variable().shape[2], - node.get_attr('filt_height'), - node.get_attr('stride_height'), - ) - instructions_str = ','.join(str(i) for i in instructions) - node.set_attr('min_height', min_h) - node.set_attr('min_width', min_w) - node.set_attr('instructions', instructions_str) - else: - node.set_attr('min_height', node.get_attr('in_height')) - node.set_attr('min_width', node.get_attr('in_width')) - node.set_attr('instructions', '0') + min_h, min_w, instructions = node.model.config.backend.compute_conv2d_instructions( + node.get_input_variable().shape[0], + node.get_input_variable().shape[1], + node.get_input_variable().shape[2], + node.get_attr('filt_height'), + node.get_attr('stride_height'), + ) + instructions_str = ','.join(str(i) for i in instructions) + node.set_attr('min_height', min_h) + node.set_attr('min_width', min_w) + node.set_attr('instructions', instructions_str) diff --git a/hls4ml/backends/vivado/passes/convolution_templates.py b/hls4ml/backends/vivado/passes/convolution_templates.py index e098107eae..8c3114ae2e 100644 --- a/hls4ml/backends/vivado/passes/convolution_templates.py +++ b/hls4ml/backends/vivado/passes/convolution_templates.py @@ -108,6 +108,11 @@ def format(self, node): else: params['conv_fn'] = 'Conv1DResource' + if node.get_attr('implementation').casefold() == 'linebuffer': + # these are unused; just put dummy values + params['min_width'] = node.get_attr('in_width') + params['instructions'] = '0' + conv_config = self.template.format(**params) mult_params = self._default_config_params(node) @@ -239,6 +244,12 @@ def format(self, node): else: params['fill_fn'] = 'FillConv2DBuffer' + if node.get_attr('implementation').casefold() == 'linebuffer': + # these are unused; just put dummy values + params['min_height'] = node.get_attr('in_height') + params['min_width'] = node.get_attr('in_width') + params['instructions'] = '0' + conv_config = self.template.format(**params) mult_params = self._default_config_params(node) From 9454e6fd70d4919e7d84cfcaabf9d37a16fe71f3 Mon Sep 17 00:00:00 2001 From: Javier Campos Date: Thu, 19 Dec 2024 12:01:01 -0600 Subject: [PATCH 4/8] format --- hls4ml/backends/vivado/passes/conv_stream.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hls4ml/backends/vivado/passes/conv_stream.py b/hls4ml/backends/vivado/passes/conv_stream.py index d7f6dca8dc..0280868e2e 100644 --- a/hls4ml/backends/vivado/passes/conv_stream.py +++ b/hls4ml/backends/vivado/passes/conv_stream.py @@ -7,7 +7,7 @@ class GenerateConvStreamingInstructions(OptimizerPass): def match(self, node): is_match = ( - isinstance(node, (Conv1D, SeparableConv1D, Conv2D, SeparableConv2D)) + isinstance(node, (Conv1D, SeparableConv1D, Conv2D, SeparableConv2D)) and node.model.config.get_config_value('IOType').casefold() == 'io_stream' and node.get_attr('implementation').casefold() == 'encoded' ) From a3ec21022042f627fe6ddaccebae04db0a19cf3d Mon Sep 17 00:00:00 2001 From: Javier Campos Date: Tue, 14 Jan 2025 16:35:43 -0600 Subject: [PATCH 5/8] Add check for io_parallel and update tests --- hls4ml/backends/vivado/passes/convolution_templates.py | 10 ++++++++-- test/pytest/test_conv1d_narrow.py | 4 ++++ test/pytest/test_conv2d_narrow.py | 4 ++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/hls4ml/backends/vivado/passes/convolution_templates.py b/hls4ml/backends/vivado/passes/convolution_templates.py index 8c3114ae2e..0e9be331ba 100644 --- a/hls4ml/backends/vivado/passes/convolution_templates.py +++ b/hls4ml/backends/vivado/passes/convolution_templates.py @@ -108,7 +108,10 @@ def format(self, node): else: params['conv_fn'] = 'Conv1DResource' - if node.get_attr('implementation').casefold() == 'linebuffer': + if ( + node.get_attr('implementation').casefold() == 'linebuffer' + or node.model.config.get_config_value('IOType').casefold() == 'io_parallel' + ): # these are unused; just put dummy values params['min_width'] = node.get_attr('in_width') params['instructions'] = '0' @@ -244,7 +247,10 @@ def format(self, node): else: params['fill_fn'] = 'FillConv2DBuffer' - if node.get_attr('implementation').casefold() == 'linebuffer': + if ( + node.get_attr('implementation').casefold() == 'linebuffer' + or node.model.config.get_config_value('IOType').casefold() == 'io_parallel' + ): # these are unused; just put dummy values params['min_height'] = node.get_attr('in_height') params['min_width'] = node.get_attr('in_width') diff --git a/test/pytest/test_conv1d_narrow.py b/test/pytest/test_conv1d_narrow.py index e3ed8f73a6..0c129fa680 100644 --- a/test/pytest/test_conv1d_narrow.py +++ b/test/pytest/test_conv1d_narrow.py @@ -31,6 +31,10 @@ def model(): ('io_stream', 'resource', 'Encoded'), ('io_stream', 'latency', 'LineBuffer'), ('io_stream', 'resource', 'LineBuffer'), + ('io_parallel', 'resource', 'Encoded'), + ('io_parallel', 'latency', 'Encoded'), + ('io_parallel', 'resource', 'LineBuffer'), + ('io_parallel', 'latency', 'LineBuffer'), ], ) @pytest.mark.filterwarnings("error") diff --git a/test/pytest/test_conv2d_narrow.py b/test/pytest/test_conv2d_narrow.py index 74042fdf30..7b98843b46 100644 --- a/test/pytest/test_conv2d_narrow.py +++ b/test/pytest/test_conv2d_narrow.py @@ -31,6 +31,10 @@ def model(): ('io_stream', 'resource', 'Encoded'), ('io_stream', 'latency', 'LineBuffer'), ('io_stream', 'resource', 'LineBuffer'), + ('io_parallel', 'resource', 'Encoded'), + ('io_parallel', 'latency', 'Encoded'), + ('io_parallel', 'resource', 'LineBuffer'), + ('io_parallel', 'latency', 'LineBuffer'), ], ) @pytest.mark.filterwarnings("error") From faa71f6f57d2e0a1688af42c718c4f99c158be8c Mon Sep 17 00:00:00 2001 From: Javier Campos Date: Fri, 17 Jan 2025 11:58:00 -0600 Subject: [PATCH 6/8] Update optimizer match and default values for linebuffer impl. --- .../backends/catapult/passes/conv_stream.py | 57 +++++++++---------- .../catapult/passes/convolution_templates.py | 17 ++++++ 2 files changed, 43 insertions(+), 31 deletions(-) diff --git a/hls4ml/backends/catapult/passes/conv_stream.py b/hls4ml/backends/catapult/passes/conv_stream.py index e0bb853d83..0280868e2e 100755 --- a/hls4ml/backends/catapult/passes/conv_stream.py +++ b/hls4ml/backends/catapult/passes/conv_stream.py @@ -6,7 +6,12 @@ class GenerateConvStreamingInstructions(OptimizerPass): '''Generates the instructions for streaming implementation of CNNs''' def match(self, node): - return isinstance(node, (Conv1D, SeparableConv1D, Conv2D, SeparableConv2D)) + is_match = ( + isinstance(node, (Conv1D, SeparableConv1D, Conv2D, SeparableConv2D)) + and node.model.config.get_config_value('IOType').casefold() == 'io_stream' + and node.get_attr('implementation').casefold() == 'encoded' + ) + return is_match def transform(self, model, node): node_class = node.__class__.__name__ @@ -18,35 +23,25 @@ def transform(self, model, node): raise Exception(f'Cannot generate instructions for node {node.name} ({node_class})') def _generate_1d_instructions(self, node): - if node.model.config.get_config_value('IOType') == 'io_stream': - min_w, instructions = node.model.config.backend.compute_conv1d_instructions( - node.get_input_variable().shape[0], - node.get_input_variable().shape[1], - node.get_attr('filt_width'), - node.get_attr('stride_width'), - ) - instructions_str = ','.join(str(i) for i in instructions) - node.set_attr('min_width', min_w) - node.set_attr('instructions', instructions_str) - else: - # these are unused; just put dummy values - node.set_attr('min_width', node.get_attr('in_width')) - node.set_attr('instructions', '0') + min_w, instructions = node.model.config.backend.compute_conv1d_instructions( + node.get_input_variable().shape[0], + node.get_input_variable().shape[1], + node.get_attr('filt_width'), + node.get_attr('stride_width'), + ) + instructions_str = ','.join(str(i) for i in instructions) + node.set_attr('min_width', min_w) + node.set_attr('instructions', instructions_str) def _generate_2d_instructions(self, node): - if node.model.config.get_config_value('IOType') == 'io_stream': - min_h, min_w, instructions = node.model.config.backend.compute_conv2d_instructions( - node.get_input_variable().shape[0], - node.get_input_variable().shape[1], - node.get_input_variable().shape[2], - node.get_attr('filt_height'), - node.get_attr('stride_height'), - ) - instructions_str = ','.join(str(i) for i in instructions) - node.set_attr('min_height', min_h) - node.set_attr('min_width', min_w) - node.set_attr('instructions', instructions_str) - else: - node.set_attr('min_height', node.get_attr('in_height')) - node.set_attr('min_width', node.get_attr('in_width')) - node.set_attr('instructions', '0') + min_h, min_w, instructions = node.model.config.backend.compute_conv2d_instructions( + node.get_input_variable().shape[0], + node.get_input_variable().shape[1], + node.get_input_variable().shape[2], + node.get_attr('filt_height'), + node.get_attr('stride_height'), + ) + instructions_str = ','.join(str(i) for i in instructions) + node.set_attr('min_height', min_h) + node.set_attr('min_width', min_w) + node.set_attr('instructions', instructions_str) diff --git a/hls4ml/backends/catapult/passes/convolution_templates.py b/hls4ml/backends/catapult/passes/convolution_templates.py index 8014a4ac8e..0ddd4ce260 100755 --- a/hls4ml/backends/catapult/passes/convolution_templates.py +++ b/hls4ml/backends/catapult/passes/convolution_templates.py @@ -94,6 +94,14 @@ def format(self, node): else: params['fill_fn'] = 'FillConv1DBuffer' + if ( + node.get_attr('implementation').casefold() == 'linebuffer' + or node.model.config.get_config_value('IOType').casefold() == 'io_parallel' + ): + # these are unused; just put dummy values + params['min_width'] = node.get_attr('in_width') + params['instructions'] = '0' + conv_config = self.template.format(**params) mult_params = self._default_config_params(node) @@ -210,6 +218,15 @@ def format(self, node): else: params['fill_fn'] = 'FillConv2DBuffer' + if ( + node.get_attr('implementation').casefold() == 'linebuffer' + or node.model.config.get_config_value('IOType').casefold() == 'io_parallel' + ): + # these are unused; just put dummy values + params['min_height'] = node.get_attr('in_height') + params['min_width'] = node.get_attr('in_width') + params['instructions'] = '0' + conv_config = self.template.format(**params) mult_params = self._default_config_params(node) From 91efc067a8be0cffa346dd9021888225d830b52e Mon Sep 17 00:00:00 2001 From: Javier Campos Date: Fri, 17 Jan 2025 12:01:56 -0600 Subject: [PATCH 7/8] format --- hls4ml/backends/catapult/passes/convolution_templates.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hls4ml/backends/catapult/passes/convolution_templates.py b/hls4ml/backends/catapult/passes/convolution_templates.py index 0ddd4ce260..d4bc8f9572 100755 --- a/hls4ml/backends/catapult/passes/convolution_templates.py +++ b/hls4ml/backends/catapult/passes/convolution_templates.py @@ -226,7 +226,7 @@ def format(self, node): params['min_height'] = node.get_attr('in_height') params['min_width'] = node.get_attr('in_width') params['instructions'] = '0' - + conv_config = self.template.format(**params) mult_params = self._default_config_params(node) From 72fea1336f5fb32f223361039cba1da3b62ee8da Mon Sep 17 00:00:00 2001 From: Javier Campos Date: Fri, 17 Jan 2025 14:48:19 -0600 Subject: [PATCH 8/8] Refactor: Replace casefold with lower and use setdefault --- .../backends/catapult/passes/conv_stream.py | 4 ++-- .../catapult/passes/convolution_templates.py | 20 +++++-------------- hls4ml/backends/vivado/passes/conv_stream.py | 4 ++-- .../vivado/passes/convolution_templates.py | 20 +++++-------------- 4 files changed, 14 insertions(+), 34 deletions(-) diff --git a/hls4ml/backends/catapult/passes/conv_stream.py b/hls4ml/backends/catapult/passes/conv_stream.py index 0280868e2e..9ba0c04d32 100755 --- a/hls4ml/backends/catapult/passes/conv_stream.py +++ b/hls4ml/backends/catapult/passes/conv_stream.py @@ -8,8 +8,8 @@ class GenerateConvStreamingInstructions(OptimizerPass): def match(self, node): is_match = ( isinstance(node, (Conv1D, SeparableConv1D, Conv2D, SeparableConv2D)) - and node.model.config.get_config_value('IOType').casefold() == 'io_stream' - and node.get_attr('implementation').casefold() == 'encoded' + and node.model.config.get_config_value('IOType').lower() == 'io_stream' + and node.get_attr('implementation').lower() == 'encoded' ) return is_match diff --git a/hls4ml/backends/catapult/passes/convolution_templates.py b/hls4ml/backends/catapult/passes/convolution_templates.py index d4bc8f9572..3dc310d5f0 100755 --- a/hls4ml/backends/catapult/passes/convolution_templates.py +++ b/hls4ml/backends/catapult/passes/convolution_templates.py @@ -94,13 +94,8 @@ def format(self, node): else: params['fill_fn'] = 'FillConv1DBuffer' - if ( - node.get_attr('implementation').casefold() == 'linebuffer' - or node.model.config.get_config_value('IOType').casefold() == 'io_parallel' - ): - # these are unused; just put dummy values - params['min_width'] = node.get_attr('in_width') - params['instructions'] = '0' + params.setdefault('min_width', node.get_attr('in_width')) + params.setdefault('instructions', '0') conv_config = self.template.format(**params) @@ -218,14 +213,9 @@ def format(self, node): else: params['fill_fn'] = 'FillConv2DBuffer' - if ( - node.get_attr('implementation').casefold() == 'linebuffer' - or node.model.config.get_config_value('IOType').casefold() == 'io_parallel' - ): - # these are unused; just put dummy values - params['min_height'] = node.get_attr('in_height') - params['min_width'] = node.get_attr('in_width') - params['instructions'] = '0' + params.setdefault('min_height', node.get_attr('in_height')) + params.setdefault('min_width', node.get_attr('in_width')) + params.setdefault('instructions', '0') conv_config = self.template.format(**params) diff --git a/hls4ml/backends/vivado/passes/conv_stream.py b/hls4ml/backends/vivado/passes/conv_stream.py index 0280868e2e..9ba0c04d32 100644 --- a/hls4ml/backends/vivado/passes/conv_stream.py +++ b/hls4ml/backends/vivado/passes/conv_stream.py @@ -8,8 +8,8 @@ class GenerateConvStreamingInstructions(OptimizerPass): def match(self, node): is_match = ( isinstance(node, (Conv1D, SeparableConv1D, Conv2D, SeparableConv2D)) - and node.model.config.get_config_value('IOType').casefold() == 'io_stream' - and node.get_attr('implementation').casefold() == 'encoded' + and node.model.config.get_config_value('IOType').lower() == 'io_stream' + and node.get_attr('implementation').lower() == 'encoded' ) return is_match diff --git a/hls4ml/backends/vivado/passes/convolution_templates.py b/hls4ml/backends/vivado/passes/convolution_templates.py index 0e9be331ba..6d0bf9e23c 100644 --- a/hls4ml/backends/vivado/passes/convolution_templates.py +++ b/hls4ml/backends/vivado/passes/convolution_templates.py @@ -108,13 +108,8 @@ def format(self, node): else: params['conv_fn'] = 'Conv1DResource' - if ( - node.get_attr('implementation').casefold() == 'linebuffer' - or node.model.config.get_config_value('IOType').casefold() == 'io_parallel' - ): - # these are unused; just put dummy values - params['min_width'] = node.get_attr('in_width') - params['instructions'] = '0' + params.setdefault('min_width', node.get_attr('in_width')) + params.setdefault('instructions', '0') conv_config = self.template.format(**params) @@ -247,14 +242,9 @@ def format(self, node): else: params['fill_fn'] = 'FillConv2DBuffer' - if ( - node.get_attr('implementation').casefold() == 'linebuffer' - or node.model.config.get_config_value('IOType').casefold() == 'io_parallel' - ): - # these are unused; just put dummy values - params['min_height'] = node.get_attr('in_height') - params['min_width'] = node.get_attr('in_width') - params['instructions'] = '0' + params.setdefault('min_height', node.get_attr('in_height')) + params.setdefault('min_width', node.get_attr('in_width')) + params.setdefault('instructions', '0') conv_config = self.template.format(**params)