@@ -40,6 +40,105 @@ namespace relay {
4040namespace contrib {
4141namespace ethosn {
4242
43+ EthosnError EthosnAPI::QnnConv2d (const Expr& expr, ConvolutionParams* params) {
44+ Call requantize = Downcast<Call>(expr);
45+ Call bias_add = Downcast<Call>(requantize->args [0 ]);
46+ Call conv = Downcast<Call>(bias_add->args [0 ]);
47+ Call pad;
48+ if (conv->args [0 ]->IsInstance <CallNode>() &&
49+ Downcast<Call>(conv->args [0 ])->op == Op::Get (" nn.pad" ))
50+ pad = Downcast<Call>(conv->args [0 ]);
51+ const auto & conv_attr = conv->attrs .as <Conv2DAttrs>();
52+
53+ // Extract the quantization params from the arguments
54+ int input_zero_point;
55+ int kernel_zero_point;
56+ int output_zero_point;
57+ float input_scale;
58+ float kernel_scale;
59+ float output_scale;
60+ EthosnError err = AsConstant<int >(conv->args [2 ], &input_zero_point);
61+ err += AsConstant<int >(conv->args [3 ], &kernel_zero_point);
62+ err += AsConstant<int >(requantize->args [4 ], &output_zero_point);
63+ err += AsConstant<float >(conv->args [4 ], &input_scale);
64+ err += AsConstant<float >(conv->args [5 ], &kernel_scale);
65+ err += AsConstant<float >(requantize->args [3 ], &output_scale);
66+
67+ // Convert quantization params
68+ sl::QuantizationInfo data_q_info;
69+ sl::QuantizationInfo weights_q_info;
70+ sl::QuantizationInfo bias_q_info;
71+ sl::QuantizationInfo output_q_info;
72+ err += Tvm2Npu (input_zero_point, input_scale, &data_q_info);
73+ err += Tvm2Npu (kernel_zero_point, kernel_scale, &weights_q_info);
74+ err += Tvm2Npu (0 , data_q_info.m_Scale * weights_q_info.m_Scale , &bias_q_info);
75+ err += Tvm2Npu (output_zero_point, output_scale, &output_q_info);
76+
77+ // Convert convolution attributes
78+ sl::Padding padding;
79+ if (pad.defined ()) {
80+ Tvm2Npu (conv_attr->padding , &padding);
81+ // Don't support both standalone operator padding and attribute defined padding
82+ if (padding != sl::Padding ({0 , 0 , 0 , 0 })) {
83+ err += EthosnError (
84+ ErrStrm () << " both op and attr padding exist, must be either op/attr only or no padding" );
85+ }
86+ err += Tvm2Npu (pad->attrs .as <PadAttrs>()->pad_width , &padding);
87+ } else {
88+ err += Tvm2Npu (conv_attr->padding , &padding);
89+ }
90+ sl::Stride stride;
91+ err += Tvm2Npu (conv_attr->strides , &stride);
92+ // Dilation is not supported
93+ std::array<uint32_t , 4 > dilation = {1 , 1 , 1 , 1 };
94+ AsArray (conv_attr->dilation , &dilation);
95+ if (conv_attr->dilation .size () != 2 || dilation[0 ] != 1 || dilation[1 ] != 1 ) {
96+ err +=
97+ EthosnError (ErrStrm () << " dilation=" << conv_attr->dilation << " , dilation must = [1, 1]" );
98+ }
99+ // Create convolution info
100+ params->conv_info = sl::ConvolutionInfo (padding, stride, output_q_info);
101+
102+ // Create data info
103+ const TensorTypeNode* data_dtype;
104+ if (pad.defined ()) {
105+ data_dtype = pad->args [0 ]->checked_type ().as <TensorTypeNode>();
106+ } else {
107+ data_dtype = conv->args [0 ]->checked_type ().as <TensorTypeNode>();
108+ }
109+ sl::TensorShape activation_tensor_shape;
110+ sl::DataType activation_data_type;
111+ err += Tvm2Npu (data_dtype->shape , &activation_tensor_shape);
112+ err += Tvm2Npu (data_dtype->dtype , &activation_data_type);
113+ params->activation_info = sl::TensorInfo (activation_tensor_shape, activation_data_type,
114+ sl::DataFormat::NHWC, data_q_info);
115+
116+ // Create weights info
117+ params->is_depthwise = conv_attr->channels .defined () &&
118+ tvm::tir::ExprDeepEqual ()(conv_attr->channels , conv_attr->groups ) &&
119+ conv_attr->groups != 1 ;
120+
121+ const auto * weights_dtype = conv->args [1 ]->checked_type ().as <TensorTypeNode>();
122+ sl::TensorShape weights_tensor_shape;
123+ sl::DataType weights_data_type;
124+ sl::DataFormat weights_data_format;
125+ // Ignore the error here because weights don't have a batch axis
126+ Tvm2Npu (weights_dtype->shape , &weights_tensor_shape);
127+ err += Tvm2Npu (weights_dtype->dtype , &weights_data_type);
128+ err += Tvm2Npu (params->is_depthwise ? " HWIM" : " HWIO" , &weights_data_format);
129+ params->weights_info =
130+ sl::TensorInfo (weights_tensor_shape, weights_data_type, weights_data_format, weights_q_info);
131+ params->raw_weights = conv->args [1 ].as <ConstantNode>()->data ->data ;
132+
133+ // Create bias info
134+ params->bias_info = sl::TensorInfo (
135+ {1 , 1 , 1 , params->is_depthwise ? weights_tensor_shape[2 ] : weights_tensor_shape[3 ]},
136+ sl::DataType::INT32_QUANTIZED, sl::DataFormat::NHWC, bias_q_info);
137+ params->raw_bias = bias_add->args [1 ].as <ConstantNode>()->data ->data ;
138+
139+ return err;
140+ }
141+
43142EthosnError EthosnAPI::Concatenate (const Expr& expr, ConcatenateParams* params) {
44143 Call call = Downcast<Call>(expr);
45144 const auto & attrs = call->attrs .as <ConcatenateAttrs>();
@@ -107,6 +206,60 @@ EthosnError EthosnAPI::Split(const Expr& expr, SplitParams* params) {
107206 return err;
108207}
109208
209+ EthosnError EthosnAPI::Tvm2Npu (const Array<IndexExpr>& padding, sl::Padding* npu_padding) {
210+ std::array<uint32_t , 4 > dim;
211+ if (EthosnError err = AsArray<IndexExpr, uint32_t >(padding, &dim)) {
212+ return err;
213+ }
214+ switch (padding.size ()) {
215+ case 1 :
216+ *npu_padding = sl::Padding (dim[0 ], dim[0 ], dim[0 ], dim[0 ]);
217+ break ;
218+ case 2 :
219+ // Height, width -> top, bottom, left, right
220+ *npu_padding = sl::Padding (dim[0 ], dim[0 ], dim[1 ], dim[1 ]);
221+ break ;
222+ case 4 :
223+ // Top, left, bottom, right -> top, bottom, left, right
224+ *npu_padding = sl::Padding (dim[0 ], dim[2 ], dim[1 ], dim[3 ]);
225+ break ;
226+ default :
227+ return EthosnError (ErrStrm () << " padding tuple size=" << padding.size ()
228+ << " , padding tuple size must be {1, 2, 4}" );
229+ }
230+ return EthosnError ();
231+ }
232+
233+ EthosnError EthosnAPI::Tvm2Npu (const Array<IndexExpr>& strides, sl::Stride* npu_stride) {
234+ if (strides.size () != 2 ) {
235+ return EthosnError (ErrStrm () << " stride size=" << strides.size () << " , stride size must = 2" );
236+ }
237+ std::array<uint32_t , 4 > dim;
238+ if (EthosnError err = AsArray<IndexExpr, uint32_t >(strides, &dim)) {
239+ return err;
240+ }
241+ *npu_stride = sl::Stride (dim[1 ], dim[0 ]);
242+ return EthosnError ();
243+ }
244+
245+ EthosnError EthosnAPI::Tvm2Npu (const std::string& dformat, sl::DataFormat* data_format) {
246+ if (dformat == " NCHW" ) {
247+ *data_format = sl::DataFormat::NCHW;
248+ return EthosnError ();
249+ } else if (dformat == " NHWC" ) {
250+ *data_format = sl::DataFormat::NHWC;
251+ return EthosnError ();
252+ } else if (dformat == " HWIO" ) {
253+ *data_format = sl::DataFormat::HWIO;
254+ return EthosnError ();
255+ } else if (dformat == " HWIM" ) {
256+ *data_format = sl::DataFormat::HWIM;
257+ return EthosnError ();
258+ }
259+ return EthosnError (ErrStrm () << " format=" << dformat
260+ << " , format must be {NCHW, NHWC, HWIO, HWIM}" );
261+ }
262+
110263EthosnError EthosnAPI::Tvm2Npu (const Array<IndexExpr>& shape, sl::TensorShape* npu_shape) {
111264 EthosnError err = AsArray<IndexExpr, uint32_t >(shape, npu_shape);
112265 if (npu_shape->front () != 1 ) {
@@ -128,6 +281,29 @@ EthosnError EthosnAPI::Tvm2Npu(const tvm::DataType& dtype, sl::DataType* data_ty
128281 return EthosnError (ErrStrm () << " dtype=\' " << dtype << " \' , dtype must be either uint8 or int32" );
129282}
130283
284+ EthosnError EthosnAPI::Tvm2Npu (int32_t zero_point, float scale, sl::QuantizationInfo* npu_qinfo) {
285+ *npu_qinfo = sl::QuantizationInfo (zero_point, scale);
286+ return EthosnError ();
287+ }
288+
289+ EthosnError EthosnAPI::Tvm2Npu (const Array<Array<Integer>>& padding, sl::Padding* npu_padding) {
290+ if (padding.size () != 4 ) {
291+ return EthosnError (ErrStrm () << " padding tuple size=" << padding.size ()
292+ << " , padding tuple size must = 4" );
293+ }
294+ Array<IndexExpr> reduced_padding;
295+ reduced_padding.push_back (padding[1 ][0 ]);
296+ reduced_padding.push_back (padding[1 ][1 ]);
297+ reduced_padding.push_back (padding[2 ][0 ]);
298+ reduced_padding.push_back (padding[2 ][1 ]);
299+ std::array<uint32_t , 4 > dim;
300+ if (EthosnError err = AsArray<IndexExpr, uint32_t >(reduced_padding, &dim)) {
301+ return err;
302+ }
303+ *npu_padding = sl::Padding (dim[0 ], dim[1 ], dim[2 ], dim[3 ]);
304+ return EthosnError ();
305+ }
306+
131307// Convert an array of IntImmNodes into ValueT
132308// IndexT type of Array indexing variable
133309// ValueT type of resulting value
@@ -158,6 +334,20 @@ EthosnError EthosnAPI::AsConstant(const Expr& expr, T* out) {
158334 return EthosnError ();
159335}
160336
337+ TVM_REGISTER_GLOBAL (" relay.ethos-n.support.conv2d" )
338+ .set_body([](tvm::TVMArgs args, tvm::TVMRetValue* rv) {
339+ Call call = args[0 ];
340+ ConvolutionParams params;
341+ auto err = EthosnAPI::QnnConv2d (call, ¶ms);
342+ if (params.is_depthwise ) {
343+ *rv = !err && sl::IsDepthwiseConvolutionSupported (params.bias_info , params.weights_info ,
344+ params.conv_info , params.activation_info );
345+ } else {
346+ *rv = !err && sl::IsConvolutionSupported (params.bias_info , params.weights_info ,
347+ params.conv_info , params.activation_info );
348+ }
349+ });
350+
161351TVM_REGISTER_GLOBAL (" relay.ethos-n.support.concatenate" )
162352 .set_body([](tvm::TVMArgs args, tvm::TVMRetValue* rv) {
163353 Call call = args[0 ];
0 commit comments