Skip to main content

nxpu_backend_arm_ethos/
support.rs

1//! Arm Ethos-U NPU operator support matrices.
2
3use nxpu_backend_core::{OperatorSupport, PerformanceTier, Precision};
4
5/// Operator support for Ethos-U55 (128 MAC configuration).
6pub struct EthosU55Support;
7
8/// Operator support for Ethos-U65 (512 MAC configuration).
9pub struct EthosU65Support;
10
11const ETHOS_NATIVE_INT8: &[&str] = &[
12    "Conv",
13    "MatMul",
14    "Add",
15    "MaxPool",
16    "AveragePool",
17    "Relu",
18    "Reshape",
19    "Concat",
20];
21
22const ETHOS_EMULATED_INT8: &[&str] = &[
23    "Mul",
24    "Sub",
25    "Sigmoid",
26    "Tanh",
27    "Softmax",
28    "BatchNormalization",
29    "Transpose",
30];
31
32/// Ops that U65 additionally supports natively (Int16).
33pub(crate) const U65_NATIVE_INT16: &[&str] = &["Conv", "MatMul", "Add", "Relu"];
34
35impl OperatorSupport for EthosU55Support {
36    fn op_support(&self, op_name: &str, precision: Precision) -> PerformanceTier {
37        match precision {
38            Precision::Int8 => {
39                if ETHOS_NATIVE_INT8.contains(&op_name) {
40                    PerformanceTier::Native
41                } else if ETHOS_EMULATED_INT8.contains(&op_name) {
42                    PerformanceTier::Emulated
43                } else {
44                    PerformanceTier::Unsupported
45                }
46            }
47            // U55 only supports Int8 natively
48            _ => {
49                if ETHOS_NATIVE_INT8.contains(&op_name) || ETHOS_EMULATED_INT8.contains(&op_name) {
50                    PerformanceTier::Emulated
51                } else {
52                    PerformanceTier::Unsupported
53                }
54            }
55        }
56    }
57
58    fn hardware_name(&self) -> &str {
59        "Arm Ethos-U55"
60    }
61
62    fn native_ops(&self) -> &[&str] {
63        ETHOS_NATIVE_INT8
64    }
65
66    fn emulated_ops(&self) -> &[&str] {
67        ETHOS_EMULATED_INT8
68    }
69}
70
71impl OperatorSupport for EthosU65Support {
72    fn op_support(&self, op_name: &str, precision: Precision) -> PerformanceTier {
73        match precision {
74            Precision::Int8 => {
75                if ETHOS_NATIVE_INT8.contains(&op_name) {
76                    PerformanceTier::Native
77                } else if ETHOS_EMULATED_INT8.contains(&op_name) {
78                    PerformanceTier::Emulated
79                } else {
80                    PerformanceTier::Unsupported
81                }
82            }
83            // U65 has limited Int16 support (mapped from F16)
84            Precision::F16 => {
85                if U65_NATIVE_INT16.contains(&op_name) {
86                    PerformanceTier::Native
87                } else if ETHOS_NATIVE_INT8.contains(&op_name)
88                    || ETHOS_EMULATED_INT8.contains(&op_name)
89                {
90                    PerformanceTier::Emulated
91                } else {
92                    PerformanceTier::Unsupported
93                }
94            }
95            _ => {
96                if ETHOS_NATIVE_INT8.contains(&op_name) || ETHOS_EMULATED_INT8.contains(&op_name) {
97                    PerformanceTier::Emulated
98                } else {
99                    PerformanceTier::Unsupported
100                }
101            }
102        }
103    }
104
105    fn hardware_name(&self) -> &str {
106        "Arm Ethos-U65"
107    }
108
109    fn native_ops(&self) -> &[&str] {
110        ETHOS_NATIVE_INT8
111    }
112
113    fn emulated_ops(&self) -> &[&str] {
114        ETHOS_EMULATED_INT8
115    }
116}
117
118#[cfg(test)]
119mod tests {
120    use super::*;
121
122    #[test]
123    fn u55_native_int8() {
124        let s = EthosU55Support;
125        assert_eq!(
126            s.op_support("Conv", Precision::Int8),
127            PerformanceTier::Native
128        );
129        assert_eq!(
130            s.op_support("MatMul", Precision::Int8),
131            PerformanceTier::Native
132        );
133        assert_eq!(
134            s.op_support("Relu", Precision::Int8),
135            PerformanceTier::Native
136        );
137    }
138
139    #[test]
140    fn u55_emulated_int8() {
141        let s = EthosU55Support;
142        assert_eq!(
143            s.op_support("Sigmoid", Precision::Int8),
144            PerformanceTier::Emulated
145        );
146    }
147
148    #[test]
149    fn u55_f16_emulated() {
150        let s = EthosU55Support;
151        assert_eq!(
152            s.op_support("Conv", Precision::F16),
153            PerformanceTier::Emulated
154        );
155    }
156
157    #[test]
158    fn u65_f16_native_for_core_ops() {
159        let s = EthosU65Support;
160        assert_eq!(
161            s.op_support("Conv", Precision::F16),
162            PerformanceTier::Native
163        );
164        assert_eq!(
165            s.op_support("MatMul", Precision::F16),
166            PerformanceTier::Native
167        );
168    }
169
170    #[test]
171    fn u65_f16_emulated_for_others() {
172        let s = EthosU65Support;
173        assert_eq!(
174            s.op_support("Sigmoid", Precision::F16),
175            PerformanceTier::Emulated
176        );
177    }
178
179    #[test]
180    fn unknown_op_unsupported() {
181        assert_eq!(
182            EthosU55Support.op_support("FakeOp", Precision::Int8),
183            PerformanceTier::Unsupported
184        );
185        assert_eq!(
186            EthosU65Support.op_support("FakeOp", Precision::Int8),
187            PerformanceTier::Unsupported
188        );
189    }
190
191    #[test]
192    fn hardware_names() {
193        assert_eq!(EthosU55Support.hardware_name(), "Arm Ethos-U55");
194        assert_eq!(EthosU65Support.hardware_name(), "Arm Ethos-U65");
195    }
196
197    #[test]
198    fn u55_bf16_emulated() {
199        let s = EthosU55Support;
200        assert_eq!(
201            s.op_support("Conv", Precision::BF16),
202            PerformanceTier::Emulated
203        );
204    }
205
206    #[test]
207    fn u55_f32_unknown_unsupported() {
208        let s = EthosU55Support;
209        assert_eq!(
210            s.op_support("FakeOp", Precision::F32),
211            PerformanceTier::Unsupported
212        );
213    }
214
215    #[test]
216    fn u65_bf16_emulated() {
217        let s = EthosU65Support;
218        assert_eq!(
219            s.op_support("Conv", Precision::BF16),
220            PerformanceTier::Emulated
221        );
222    }
223
224    #[test]
225    fn u65_f32_emulated() {
226        let s = EthosU65Support;
227        assert_eq!(
228            s.op_support("Conv", Precision::F32),
229            PerformanceTier::Emulated
230        );
231    }
232
233    #[test]
234    fn u65_f16_unsupported() {
235        let s = EthosU65Support;
236        assert_eq!(
237            s.op_support("FakeOp", Precision::F16),
238            PerformanceTier::Unsupported
239        );
240    }
241
242    #[test]
243    fn u65_int8_emulated() {
244        let s = EthosU65Support;
245        assert_eq!(
246            s.op_support("Sigmoid", Precision::Int8),
247            PerformanceTier::Emulated
248        );
249    }
250}