blob: e52b489618e98509ef81a81ad096f2f251451935 [file] [log] [blame]
Tim Hall3b1578e2023-01-13 17:57:25 +00001# SPDX-FileCopyrightText: Copyright 2020-2021, 2023 Arm Limited and/or its affiliates <open-source-office@arm.com>
Louis Verhaard0b8268a2020-08-05 16:11:29 +02002#
3# SPDX-License-Identifier: Apache-2.0
4#
5# Licensed under the Apache License, Version 2.0 (the License); you may
6# not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an AS IS BASIS, WITHOUT
13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
Rickard Bolinbc6ee582022-11-04 08:24:29 +000016#
Louis Verhaard0b8268a2020-08-05 16:11:29 +020017# Description:
18# Unit tests for LUT support
Jacob Bohlin1a666972020-09-11 10:04:15 +020019import random
20
Louis Verhaard0b8268a2020-08-05 16:11:29 +020021from ethosu.vela import lut
22from ethosu.vela import mark_tensors
23from ethosu.vela import pass_packing
24from ethosu.vela.data_type import DataType
25from ethosu.vela.high_level_command_stream import DMA
26from ethosu.vela.nn_graph import Graph
Louis Verhaardaee5d752020-09-30 09:01:52 +020027from ethosu.vela.operation import Op
Tim Halld8339a72021-05-27 18:49:40 +010028from ethosu.vela.rewrite_graph import rewrite_graph_pre_order
Louis Verhaard0b8268a2020-08-05 16:11:29 +020029from ethosu.vela.rewrite_graph import verify_graph_health
30from ethosu.vela.tensor import create_const_tensor
31from ethosu.vela.tensor import TensorPurpose
32from ethosu.vela.test import testutil
33
34
Tim Halld8339a72021-05-27 18:49:40 +010035def set_256_lut(op, key, arch):
Jacob Bohlin1a666972020-09-11 10:04:15 +020036 random.seed(key)
37 values = random.choices(range(256), k=256)
Tim Hall1c590482023-01-26 17:27:00 +000038 lut_tensor = create_const_tensor(op.name + "_lut", [1, 1, 1, 256], DataType.uint8, values, TensorPurpose.LUT)
Johan Alfven126558e2023-03-09 08:36:10 +010039 scratch_lut_tensor = lut_tensor.clone_into_shram(arch)
Tim Halld8339a72021-05-27 18:49:40 +010040 op.set_activation_lut(scratch_lut_tensor)
Louis Verhaard0b8268a2020-08-05 16:11:29 +020041
42
Tim Halld8339a72021-05-27 18:49:40 +010043def set_1K_lut(op, key, arch):
Jacob Bohlin1a666972020-09-11 10:04:15 +020044 random.seed(key)
45 values = random.choices(range(256), k=256)
Tim Hall3b1578e2023-01-13 17:57:25 +000046 lut_tensor = create_const_tensor(op.name + "_lut", [1, 1, 1, 256], DataType.int32, values, TensorPurpose.LUT)
Johan Alfven126558e2023-03-09 08:36:10 +010047 scratch_lut_tensor = lut_tensor.clone_into_shram(arch)
Tim Halld8339a72021-05-27 18:49:40 +010048 op.set_activation_lut(scratch_lut_tensor)
Louis Verhaard0b8268a2020-08-05 16:11:29 +020049
50
Tim Halld8339a72021-05-27 18:49:40 +010051def set_2K_lut(op, key, arch):
Jacob Bohlin1a666972020-09-11 10:04:15 +020052 random.seed(key)
53 values = random.choices(range(512), k=512)
Tim Hall3b1578e2023-01-13 17:57:25 +000054 lut_tensor = create_const_tensor(op.name + "_lut", [1, 1, 1, 512], DataType.int32, values, TensorPurpose.LUT)
Johan Alfven126558e2023-03-09 08:36:10 +010055 scratch_lut_tensor = lut_tensor.clone_into_shram(arch)
Tim Halld8339a72021-05-27 18:49:40 +010056 op.set_activation_lut(scratch_lut_tensor)
Louis Verhaard0b8268a2020-08-05 16:11:29 +020057
58
59def process(arch, op_list):
60 # Returns subgraph with given operations
61 nng = Graph()
62 sg = testutil.create_subgraph(op_list)
63 nng.subgraphs.append(sg)
64 assert verify_graph_health(nng)
65 nng = mark_tensors.mark_tensor_purpose(nng, arch, False)
66 assert verify_graph_health(nng)
Tim Halld8339a72021-05-27 18:49:40 +010067 rewrite_graph_pre_order(nng, sg, arch, [], [])
Louis Verhaard0b8268a2020-08-05 16:11:29 +020068 pass_packing.pack_into_passes(nng, arch, False)
69 assert verify_graph_health(nng)
70 # Create a DMA instruction for every op
71 cmd_list = []
72 for ps in sg.passes:
Tim Halld8339a72021-05-27 18:49:40 +010073 for input_tens in ps.inputs:
74 if input_tens.src_tensor:
75 cmd_list.append(DMA(ps, input_tens.src_tensor, input_tens, None))
76
Louis Verhaard0b8268a2020-08-05 16:11:29 +020077 sg.high_level_command_stream = cmd_list
78 return sg
79
80
Patrik Gustavssone5cf95b2020-09-03 16:39:52 +020081def filter_lut_cmds(cmd_list):
82 lut_cmd_list = []
83 for cmd in cmd_list:
84 if "lut" in cmd.in_tensor.name:
85 lut_cmd_list.append(cmd)
86 return lut_cmd_list
87
88
Louis Verhaard0b8268a2020-08-05 16:11:29 +020089def test_optimize_high_level_cmd_stream_2K():
90 # Tests lut.optimize_high_level_cmd_stream, blending 256 byte and 2K luts
91 arch = testutil.create_arch()
92 shape = [1, 1, 1, 1]
93 # u8 LUT op, should lead to DMA
Louis Verhaardaee5d752020-09-30 09:01:52 +020094 op0 = testutil.create_elemwise_op(Op.Add, "op0", shape, shape, shape)
Tim Halld8339a72021-05-27 18:49:40 +010095 set_256_lut(op0, "lut0", arch)
Louis Verhaard0b8268a2020-08-05 16:11:29 +020096 # u8 LUT op, should lead to DMA
Louis Verhaardaee5d752020-09-30 09:01:52 +020097 op1 = testutil.create_elemwise_op(Op.Add, "op1", shape, shape, shape)
Tim Halld8339a72021-05-27 18:49:40 +010098 set_256_lut(op1, "lut1", arch)
Louis Verhaard0b8268a2020-08-05 16:11:29 +020099 # u8 LUT op with different LUT, should lead to DMA
Louis Verhaardaee5d752020-09-30 09:01:52 +0200100 op2 = testutil.create_elemwise_op(Op.Add, "op2", shape, shape, shape)
Tim Halld8339a72021-05-27 18:49:40 +0100101 set_256_lut(op2, "lut2", arch)
Louis Verhaard0b8268a2020-08-05 16:11:29 +0200102 # u8 LUT op with same LUT as in op1, should not lead to DMA
Louis Verhaardaee5d752020-09-30 09:01:52 +0200103 op3 = testutil.create_elemwise_op(Op.Add, "op3", shape, shape, shape)
Tim Halld8339a72021-05-27 18:49:40 +0100104 set_256_lut(op3, "lut1", arch)
Louis Verhaard0b8268a2020-08-05 16:11:29 +0200105 # u8 LUT op with same LUT as in op2, should not lead to DMA
Louis Verhaardaee5d752020-09-30 09:01:52 +0200106 op4 = testutil.create_elemwise_op(Op.Add, "op4", shape, shape, shape)
Tim Halld8339a72021-05-27 18:49:40 +0100107 set_256_lut(op4, "lut2", arch)
Louis Verhaard0b8268a2020-08-05 16:11:29 +0200108 # 2K LUT op, should lead to DMA, and will overwrite all previous LUTs in SHRAM
Louis Verhaardaee5d752020-09-30 09:01:52 +0200109 op5_2K = testutil.create_elemwise_op(Op.Add, "op5", shape, shape, shape)
Tim Halld8339a72021-05-27 18:49:40 +0100110 set_2K_lut(op5_2K, "lut5", arch)
Louis Verhaard0b8268a2020-08-05 16:11:29 +0200111 # Another 2K LUT op, should lead to DMA, and will overwrite the previous LUT in SHRAM
Louis Verhaardaee5d752020-09-30 09:01:52 +0200112 op6_2K = testutil.create_elemwise_op(Op.Add, "op6", shape, shape, shape)
Tim Halld8339a72021-05-27 18:49:40 +0100113 set_2K_lut(op6_2K, "lut6", arch)
Louis Verhaard0b8268a2020-08-05 16:11:29 +0200114 # u8 LUT op with same LUT as in op1, should lead to DMA
Louis Verhaardaee5d752020-09-30 09:01:52 +0200115 op7 = testutil.create_elemwise_op(Op.Add, "op7", shape, shape, shape)
Tim Halld8339a72021-05-27 18:49:40 +0100116 set_256_lut(op7, "lut1", arch)
Louis Verhaard0b8268a2020-08-05 16:11:29 +0200117
118 op_list = [op0, op1, op2, op3, op4, op5_2K, op6_2K, op7]
119 sg = process(arch, op_list)
120 orig_cmd_list = sg.high_level_command_stream
121 sg.high_level_command_stream = orig_cmd_list
122 lut.optimize_high_level_cmd_stream(sg, arch)
123 cmd_list = sg.high_level_command_stream
124 # Check that only the needed DMA commands are left
125 expected_dma_ops = [op0, op1, op2, op5_2K, op6_2K, op7]
Patrik Gustavssone5cf95b2020-09-03 16:39:52 +0200126
127 cmd_list = filter_lut_cmds(cmd_list)
128 orig_cmd_list = filter_lut_cmds(orig_cmd_list)
129
Louis Verhaard0b8268a2020-08-05 16:11:29 +0200130 for (cmd, op) in zip(cmd_list, expected_dma_ops):
Tim Halld8339a72021-05-27 18:49:40 +0100131 assert cmd.in_tensor == op.activation_lut.src_tensor
Louis Verhaard0b8268a2020-08-05 16:11:29 +0200132 # Check that lut0, lut1 and lut2 in op0, op1, op2 are stored on different addresses
133 assert orig_cmd_list[0].out_tensor.address != orig_cmd_list[1].out_tensor.address
134 assert orig_cmd_list[0].out_tensor.address != orig_cmd_list[2].out_tensor.address
135 assert orig_cmd_list[1].out_tensor.address != orig_cmd_list[2].out_tensor.address
136 # Check that lut1 in op1 and op3 have same address
137 assert orig_cmd_list[1].out_tensor.address == orig_cmd_list[3].out_tensor.address
138 # Check that lut2 in op2 and op4 have same address
139 assert orig_cmd_list[2].out_tensor.address == orig_cmd_list[4].out_tensor.address
140 # Check that lut-s for 16 bit (op5 and op6) are stored on same address
141 assert orig_cmd_list[5].out_tensor.address == orig_cmd_list[6].out_tensor.address
142
143
144def test_optimize_high_level_cmd_stream_1K():
145 # Tests lut.optimize_high_level_cmd_stream, blending 256 and 1K luts
146 arch = testutil.create_arch()
147 shape = [1, 1, 1, 1]
148 # u8 LUT op, should lead to DMA
Louis Verhaardaee5d752020-09-30 09:01:52 +0200149 op0 = testutil.create_elemwise_op(Op.Add, "op0", shape, shape, shape)
Tim Halld8339a72021-05-27 18:49:40 +0100150 set_256_lut(op0, "lut0", arch)
Louis Verhaard0b8268a2020-08-05 16:11:29 +0200151 # u8 LUT op, should lead to DMA
Louis Verhaardaee5d752020-09-30 09:01:52 +0200152 op1 = testutil.create_elemwise_op(Op.Add, "op1", shape, shape, shape)
Tim Halld8339a72021-05-27 18:49:40 +0100153 set_256_lut(op1, "lut1", arch)
Louis Verhaard0b8268a2020-08-05 16:11:29 +0200154 # 1K LUT op with different LUT, should lead to DMA
Louis Verhaardaee5d752020-09-30 09:01:52 +0200155 op2_1K = testutil.create_elemwise_op(Op.Add, "op2", shape, shape, shape)
Tim Halld8339a72021-05-27 18:49:40 +0100156 set_1K_lut(op2_1K, "lut2", arch)
Louis Verhaard0b8268a2020-08-05 16:11:29 +0200157 # u8 LUT op with same LUT as in op1, should not lead to DMA
Louis Verhaardaee5d752020-09-30 09:01:52 +0200158 op3 = testutil.create_elemwise_op(Op.Add, "op3", shape, shape, shape)
Tim Halld8339a72021-05-27 18:49:40 +0100159 set_256_lut(op3, "lut1", arch)
Louis Verhaard0b8268a2020-08-05 16:11:29 +0200160 # 1K LUT op with same LUT as in op2, should not lead to DMA
Louis Verhaardaee5d752020-09-30 09:01:52 +0200161 op4_1K = testutil.create_elemwise_op(Op.Add, "op4", shape, shape, shape)
Tim Halld8339a72021-05-27 18:49:40 +0100162 set_1K_lut(op4_1K, "lut2", arch)
Louis Verhaard0b8268a2020-08-05 16:11:29 +0200163 # 1K LUT op, should lead to DMA, and will overwrite lut2
Louis Verhaardaee5d752020-09-30 09:01:52 +0200164 op5_2K = testutil.create_elemwise_op(Op.Add, "op5", shape, shape, shape)
Tim Halld8339a72021-05-27 18:49:40 +0100165 set_1K_lut(op5_2K, "lut5", arch)
Louis Verhaard0b8268a2020-08-05 16:11:29 +0200166 # u8 LUT op, lut0 should still be present, should not lead to DMA
Louis Verhaardaee5d752020-09-30 09:01:52 +0200167 op6 = testutil.create_elemwise_op(Op.Add, "op6", shape, shape, shape)
Tim Halld8339a72021-05-27 18:49:40 +0100168 set_256_lut(op6, "lut0", arch)
Louis Verhaard0b8268a2020-08-05 16:11:29 +0200169 # 1K LUT op with same LUT as in op2, should lead to DMA
Louis Verhaardaee5d752020-09-30 09:01:52 +0200170 op7 = testutil.create_elemwise_op(Op.Add, "op7", shape, shape, shape)
Tim Halld8339a72021-05-27 18:49:40 +0100171 set_1K_lut(op7, "lut2", arch)
Louis Verhaard0b8268a2020-08-05 16:11:29 +0200172
173 op_list = [op0, op1, op2_1K, op3, op4_1K, op5_2K, op6, op7]
174 sg = process(arch, op_list)
175 orig_cmd_list = sg.high_level_command_stream
176 sg.high_level_command_stream = orig_cmd_list
177 lut.optimize_high_level_cmd_stream(sg, arch)
178 cmd_list = sg.high_level_command_stream
Patrik Gustavssone5cf95b2020-09-03 16:39:52 +0200179
180 cmd_list = filter_lut_cmds(cmd_list)
181 orig_cmd_list = filter_lut_cmds(orig_cmd_list)
182
Louis Verhaard0b8268a2020-08-05 16:11:29 +0200183 # Check that only the needed DMA commands are left
184 expected_dma_ops = [op0, op1, op2_1K, op5_2K, op7]
185 for (cmd, op) in zip(cmd_list, expected_dma_ops):
Tim Halld8339a72021-05-27 18:49:40 +0100186 assert cmd.in_tensor == op.activation_lut.src_tensor
Louis Verhaard0b8268a2020-08-05 16:11:29 +0200187 # Check that lut0, lut1 and lut2 in op0, op1, op2 are stored on different addresses
188 assert orig_cmd_list[0].out_tensor.address != orig_cmd_list[1].out_tensor.address
189 assert orig_cmd_list[0].out_tensor.address != orig_cmd_list[2].out_tensor.address
190 assert orig_cmd_list[1].out_tensor.address != orig_cmd_list[2].out_tensor.address
191 # Check that lut1 in op1 and op3 have same address
192 assert orig_cmd_list[1].out_tensor.address == orig_cmd_list[3].out_tensor.address
193 # Check that lut2 in op2 and op4 and op7 have same address
194 assert orig_cmd_list[2].out_tensor.address == orig_cmd_list[4].out_tensor.address
195 assert orig_cmd_list[2].out_tensor.address == orig_cmd_list[7].out_tensor.address