blob: 44ee0afb5299b32ab9ec0a6eb4ae52ce8dcf72e7 [file] [log] [blame]
Louis Verhaard0b8268a2020-08-05 16:11:29 +02001# Copyright (C) 2020 Arm Limited or its affiliates. All rights reserved.
2#
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.
16# Description:
17# Unit tests for LUT support
Jacob Bohlin1a666972020-09-11 10:04:15 +020018import random
19
Louis Verhaard0b8268a2020-08-05 16:11:29 +020020import numpy as np
21
22from ethosu.vela import insert_dma
23from ethosu.vela import lut
24from ethosu.vela import mark_tensors
25from ethosu.vela import pass_packing
26from ethosu.vela.data_type import DataType
27from ethosu.vela.high_level_command_stream import DMA
28from ethosu.vela.nn_graph import Graph
Louis Verhaardaee5d752020-09-30 09:01:52 +020029from ethosu.vela.operation import Op
Louis Verhaard0b8268a2020-08-05 16:11:29 +020030from ethosu.vela.rewrite_graph import verify_graph_health
31from ethosu.vela.tensor import create_const_tensor
32from ethosu.vela.tensor import TensorPurpose
33from ethosu.vela.test import testutil
34
35
36def set_256_lut(op, key):
Jacob Bohlin1a666972020-09-11 10:04:15 +020037 random.seed(key)
38 values = random.choices(range(256), k=256)
Louis Verhaard0b8268a2020-08-05 16:11:29 +020039 lut_tensor = create_const_tensor(
40 op.name + "_lut", [1, 1, 1, 256], DataType.int8, values, np.uint8, TensorPurpose.LUT
41 )
Louis Verhaard0b8268a2020-08-05 16:11:29 +020042 op.set_activation_lut(lut_tensor)
43
44
45def set_1K_lut(op, key):
Jacob Bohlin1a666972020-09-11 10:04:15 +020046 random.seed(key)
47 values = random.choices(range(256), k=256)
Louis Verhaard0b8268a2020-08-05 16:11:29 +020048 lut_tensor = create_const_tensor(
49 op.name + "_lut", [1, 1, 1, 256], DataType.int32, values, np.uint32, TensorPurpose.LUT
50 )
Louis Verhaard0b8268a2020-08-05 16:11:29 +020051 op.set_activation_lut(lut_tensor)
52
53
54def set_2K_lut(op, key):
Jacob Bohlin1a666972020-09-11 10:04:15 +020055 random.seed(key)
56 values = random.choices(range(512), k=512)
Louis Verhaard0b8268a2020-08-05 16:11:29 +020057 lut_tensor = create_const_tensor(
58 op.name + "_lut", [1, 1, 1, 512], DataType.int32, values, np.uint32, TensorPurpose.LUT
59 )
Louis Verhaard0b8268a2020-08-05 16:11:29 +020060 op.set_activation_lut(lut_tensor)
61
62
63def process(arch, op_list):
64 # Returns subgraph with given operations
65 nng = Graph()
66 sg = testutil.create_subgraph(op_list)
67 nng.subgraphs.append(sg)
68 assert verify_graph_health(nng)
69 nng = mark_tensors.mark_tensor_purpose(nng, arch, False)
70 assert verify_graph_health(nng)
71 nng = insert_dma.insert_dma_commands(nng, arch, False)
72 assert verify_graph_health(nng)
73 pass_packing.pack_into_passes(nng, arch, False)
74 assert verify_graph_health(nng)
75 # Create a DMA instruction for every op
76 cmd_list = []
77 for ps in sg.passes:
78 for intermediate in ps.intermediates:
79 if intermediate.needs_dma():
80 cmd_list.append(DMA(ps, intermediate.get_dma_src_tensor(), intermediate, None))
81 sg.high_level_command_stream = cmd_list
82 return sg
83
84
Patrik Gustavssone5cf95b2020-09-03 16:39:52 +020085def filter_lut_cmds(cmd_list):
86 lut_cmd_list = []
87 for cmd in cmd_list:
88 if "lut" in cmd.in_tensor.name:
89 lut_cmd_list.append(cmd)
90 return lut_cmd_list
91
92
Louis Verhaard0b8268a2020-08-05 16:11:29 +020093def test_optimize_high_level_cmd_stream_2K():
94 # Tests lut.optimize_high_level_cmd_stream, blending 256 byte and 2K luts
95 arch = testutil.create_arch()
96 shape = [1, 1, 1, 1]
97 # u8 LUT op, should lead to DMA
Louis Verhaardaee5d752020-09-30 09:01:52 +020098 op0 = testutil.create_elemwise_op(Op.Add, "op0", shape, shape, shape)
Louis Verhaard0b8268a2020-08-05 16:11:29 +020099 set_256_lut(op0, "lut0")
100 # u8 LUT op, should lead to DMA
Louis Verhaardaee5d752020-09-30 09:01:52 +0200101 op1 = testutil.create_elemwise_op(Op.Add, "op1", shape, shape, shape)
Louis Verhaard0b8268a2020-08-05 16:11:29 +0200102 set_256_lut(op1, "lut1")
103 # u8 LUT op with different LUT, should lead to DMA
Louis Verhaardaee5d752020-09-30 09:01:52 +0200104 op2 = testutil.create_elemwise_op(Op.Add, "op2", shape, shape, shape)
Louis Verhaard0b8268a2020-08-05 16:11:29 +0200105 set_256_lut(op2, "lut2")
106 # u8 LUT op with same LUT as in op1, should not lead to DMA
Louis Verhaardaee5d752020-09-30 09:01:52 +0200107 op3 = testutil.create_elemwise_op(Op.Add, "op3", shape, shape, shape)
Louis Verhaard0b8268a2020-08-05 16:11:29 +0200108 set_256_lut(op3, "lut1")
109 # u8 LUT op with same LUT as in op2, should not lead to DMA
Louis Verhaardaee5d752020-09-30 09:01:52 +0200110 op4 = testutil.create_elemwise_op(Op.Add, "op4", shape, shape, shape)
Louis Verhaard0b8268a2020-08-05 16:11:29 +0200111 set_256_lut(op4, "lut2")
112 # 2K LUT op, should lead to DMA, and will overwrite all previous LUTs in SHRAM
Louis Verhaardaee5d752020-09-30 09:01:52 +0200113 op5_2K = testutil.create_elemwise_op(Op.Add, "op5", shape, shape, shape)
Louis Verhaard0b8268a2020-08-05 16:11:29 +0200114 set_2K_lut(op5_2K, "lut5")
115 # Another 2K LUT op, should lead to DMA, and will overwrite the previous LUT in SHRAM
Louis Verhaardaee5d752020-09-30 09:01:52 +0200116 op6_2K = testutil.create_elemwise_op(Op.Add, "op6", shape, shape, shape)
Louis Verhaard0b8268a2020-08-05 16:11:29 +0200117 set_2K_lut(op6_2K, "lut6")
118 # u8 LUT op with same LUT as in op1, should lead to DMA
Louis Verhaardaee5d752020-09-30 09:01:52 +0200119 op7 = testutil.create_elemwise_op(Op.Add, "op7", shape, shape, shape)
Louis Verhaard0b8268a2020-08-05 16:11:29 +0200120 set_256_lut(op7, "lut1")
121
122 op_list = [op0, op1, op2, op3, op4, op5_2K, op6_2K, op7]
123 sg = process(arch, op_list)
124 orig_cmd_list = sg.high_level_command_stream
125 sg.high_level_command_stream = orig_cmd_list
126 lut.optimize_high_level_cmd_stream(sg, arch)
127 cmd_list = sg.high_level_command_stream
128 # Check that only the needed DMA commands are left
129 expected_dma_ops = [op0, op1, op2, op5_2K, op6_2K, op7]
Patrik Gustavssone5cf95b2020-09-03 16:39:52 +0200130
131 cmd_list = filter_lut_cmds(cmd_list)
132 orig_cmd_list = filter_lut_cmds(orig_cmd_list)
133
Louis Verhaard0b8268a2020-08-05 16:11:29 +0200134 for (cmd, op) in zip(cmd_list, expected_dma_ops):
135 assert cmd.in_tensor == op.activation_lut
136 # Check that lut0, lut1 and lut2 in op0, op1, op2 are stored on different addresses
137 assert orig_cmd_list[0].out_tensor.address != orig_cmd_list[1].out_tensor.address
138 assert orig_cmd_list[0].out_tensor.address != orig_cmd_list[2].out_tensor.address
139 assert orig_cmd_list[1].out_tensor.address != orig_cmd_list[2].out_tensor.address
140 # Check that lut1 in op1 and op3 have same address
141 assert orig_cmd_list[1].out_tensor.address == orig_cmd_list[3].out_tensor.address
142 # Check that lut2 in op2 and op4 have same address
143 assert orig_cmd_list[2].out_tensor.address == orig_cmd_list[4].out_tensor.address
144 # Check that lut-s for 16 bit (op5 and op6) are stored on same address
145 assert orig_cmd_list[5].out_tensor.address == orig_cmd_list[6].out_tensor.address
146
147
148def test_optimize_high_level_cmd_stream_1K():
149 # Tests lut.optimize_high_level_cmd_stream, blending 256 and 1K luts
150 arch = testutil.create_arch()
151 shape = [1, 1, 1, 1]
152 # u8 LUT op, should lead to DMA
Louis Verhaardaee5d752020-09-30 09:01:52 +0200153 op0 = testutil.create_elemwise_op(Op.Add, "op0", shape, shape, shape)
Louis Verhaard0b8268a2020-08-05 16:11:29 +0200154 set_256_lut(op0, "lut0")
155 # u8 LUT op, should lead to DMA
Louis Verhaardaee5d752020-09-30 09:01:52 +0200156 op1 = testutil.create_elemwise_op(Op.Add, "op1", shape, shape, shape)
Louis Verhaard0b8268a2020-08-05 16:11:29 +0200157 set_256_lut(op1, "lut1")
158 # 1K LUT op with different LUT, should lead to DMA
Louis Verhaardaee5d752020-09-30 09:01:52 +0200159 op2_1K = testutil.create_elemwise_op(Op.Add, "op2", shape, shape, shape)
Louis Verhaard0b8268a2020-08-05 16:11:29 +0200160 set_1K_lut(op2_1K, "lut2")
161 # u8 LUT op with same LUT as in op1, should not lead to DMA
Louis Verhaardaee5d752020-09-30 09:01:52 +0200162 op3 = testutil.create_elemwise_op(Op.Add, "op3", shape, shape, shape)
Louis Verhaard0b8268a2020-08-05 16:11:29 +0200163 set_256_lut(op3, "lut1")
164 # 1K LUT op with same LUT as in op2, should not lead to DMA
Louis Verhaardaee5d752020-09-30 09:01:52 +0200165 op4_1K = testutil.create_elemwise_op(Op.Add, "op4", shape, shape, shape)
Louis Verhaard0b8268a2020-08-05 16:11:29 +0200166 set_1K_lut(op4_1K, "lut2")
167 # 1K LUT op, should lead to DMA, and will overwrite lut2
Louis Verhaardaee5d752020-09-30 09:01:52 +0200168 op5_2K = testutil.create_elemwise_op(Op.Add, "op5", shape, shape, shape)
Louis Verhaard0b8268a2020-08-05 16:11:29 +0200169 set_1K_lut(op5_2K, "lut5")
170 # u8 LUT op, lut0 should still be present, should not lead to DMA
Louis Verhaardaee5d752020-09-30 09:01:52 +0200171 op6 = testutil.create_elemwise_op(Op.Add, "op6", shape, shape, shape)
Louis Verhaard0b8268a2020-08-05 16:11:29 +0200172 set_256_lut(op6, "lut0")
173 # 1K LUT op with same LUT as in op2, should lead to DMA
Louis Verhaardaee5d752020-09-30 09:01:52 +0200174 op7 = testutil.create_elemwise_op(Op.Add, "op7", shape, shape, shape)
Louis Verhaard0b8268a2020-08-05 16:11:29 +0200175 set_1K_lut(op7, "lut2")
176
177 op_list = [op0, op1, op2_1K, op3, op4_1K, op5_2K, op6, op7]
178 sg = process(arch, op_list)
179 orig_cmd_list = sg.high_level_command_stream
180 sg.high_level_command_stream = orig_cmd_list
181 lut.optimize_high_level_cmd_stream(sg, arch)
182 cmd_list = sg.high_level_command_stream
Patrik Gustavssone5cf95b2020-09-03 16:39:52 +0200183
184 cmd_list = filter_lut_cmds(cmd_list)
185 orig_cmd_list = filter_lut_cmds(orig_cmd_list)
186
Louis Verhaard0b8268a2020-08-05 16:11:29 +0200187 # Check that only the needed DMA commands are left
188 expected_dma_ops = [op0, op1, op2_1K, op5_2K, op7]
189 for (cmd, op) in zip(cmd_list, expected_dma_ops):
190 assert cmd.in_tensor == op.activation_lut
191 # Check that lut0, lut1 and lut2 in op0, op1, op2 are stored on different addresses
192 assert orig_cmd_list[0].out_tensor.address != orig_cmd_list[1].out_tensor.address
193 assert orig_cmd_list[0].out_tensor.address != orig_cmd_list[2].out_tensor.address
194 assert orig_cmd_list[1].out_tensor.address != orig_cmd_list[2].out_tensor.address
195 # Check that lut1 in op1 and op3 have same address
196 assert orig_cmd_list[1].out_tensor.address == orig_cmd_list[3].out_tensor.address
197 # Check that lut2 in op2 and op4 and op7 have same address
198 assert orig_cmd_list[2].out_tensor.address == orig_cmd_list[4].out_tensor.address
199 assert orig_cmd_list[2].out_tensor.address == orig_cmd_list[7].out_tensor.address