1 /*
2  * Copyright (C) 2010-2018 Arm Limited or its affiliates. All rights reserved.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Licensed under the Apache License, Version 2.0 (the License); you may
7  * not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an AS IS BASIS, WITHOUT
14  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 
19 /* ----------------------------------------------------------------------
20  * Project:      CMSIS NN Library
21  * Title:        arm_fully_connected_mat_q7_vec_q15.c
22  * Description:  Mixed Q15-Q7 fully-connected layer function
23  *
24  * $Date:        17. January 2018
25  * $Revision:    V.1.0.0
26  *
27  * Target Processor:  Cortex-M cores
28  *
29  * -------------------------------------------------------------------- */
30 
31 #include "arm_math.h"
32 #include "arm_nnfunctions.h"
33 
34 /**
35  *  @ingroup groupNN
36  */
37 
38 /**
39  * @addtogroup FC
40  * @{
41  */
42 
43   /**
44    * @brief Mixed Q15-Q7 fully-connected layer function
45    * @param[in]       pV          pointer to input vector
46    * @param[in]       pM          pointer to matrix weights
47    * @param[in]       dim_vec     length of the vector
48    * @param[in]       num_of_rows number of rows in weight matrix
49    * @param[in]       bias_shift  amount of left-shift for bias
50    * @param[in]       out_shift   amount of right-shift for output
51    * @param[in]       bias        pointer to bias
52    * @param[in,out]   pOut        pointer to output vector
53    * @param[in,out]   vec_buffer  pointer to buffer space for input
54    * @return     The function returns <code>ARM_MATH_SUCCESS</code>
55    *
56    * @details
57    *
58    * <b>Buffer size:</b>
59    *
60    * vec_buffer size: 0
61    *
62    *  Q7_Q15 version of the fully connected layer
63    *
64    *  Weights are in q7_t and Activations are in q15_t
65    *
66    */
67 
68 arm_status
arm_fully_connected_mat_q7_vec_q15(const q15_t * pV,const q7_t * pM,const uint16_t dim_vec,const uint16_t num_of_rows,const uint16_t bias_shift,const uint16_t out_shift,const q7_t * bias,q15_t * pOut,q15_t * vec_buffer)69 arm_fully_connected_mat_q7_vec_q15(const q15_t * pV,
70                                    const q7_t * pM,
71                                    const uint16_t dim_vec,
72                                    const uint16_t num_of_rows,
73                                    const uint16_t bias_shift,
74                                    const uint16_t out_shift,
75                                    const q7_t * bias,
76                                    q15_t * pOut,
77                                    q15_t * vec_buffer)
78 {
79 
80 #if defined (ARM_MATH_DSP)
81     /* Run the following code for Cortex-M4 and Cortex-M7 */
82 
83     const q7_t *pB = pM;
84     const q7_t *pB2;
85     q15_t    *pO = pOut;
86     const q7_t *pBias = bias;
87     const q15_t *pA = pV;
88 
89     uint16_t  rowCnt = num_of_rows >> 1;
90 
91     while (rowCnt)
92     {
93         q31_t     sum =  ((q31_t)(*pBias++) << bias_shift) + NN_ROUND(out_shift);
94         q31_t     sum2 = ((q31_t)(*pBias++) << bias_shift) + NN_ROUND(out_shift);
95         uint16_t  colCnt = dim_vec >> 2;
96 
97         pA = pV;
98         pB2 = pB + dim_vec;
99 
100         while (colCnt)
101         {
102             q31_t     inV, inM11, inM12, inM21, inM22;
103             pB = (q7_t *) read_and_pad((void *)pB, &inM11, &inM12);
104             pB2 = (q7_t *) read_and_pad((void *)pB2, &inM21, &inM22);
105 
106             inV = *__SIMD32(pA)++;
107 
108             sum = __SMLAD(inV, inM11, sum);
109             sum2 = __SMLAD(inV, inM21, sum2);
110 
111             inV = *__SIMD32(pA)++;
112 
113             sum = __SMLAD(inV, inM12, sum);
114             sum2 = __SMLAD(inV, inM22, sum2);
115 
116             colCnt--;
117         }
118         colCnt = dim_vec & 0x3;
119         while (colCnt)
120         {
121             q15_t     inV = *pA++;
122             q7_t      inM = *pB++;
123             q7_t      inM2 = *pB2++;
124 
125             sum += inV * inM;
126             sum2 += inV * inM2;
127             colCnt--;
128         }                       /* while over colCnt */
129         *pO++ = (q15_t) (__SSAT((sum >> out_shift), 16));
130         *pO++ = (q15_t) (__SSAT((sum2 >> out_shift), 16));
131 
132         /*adjust the pointers and counters */
133         pB += dim_vec;
134         rowCnt--;
135     }
136 
137     /* left-over part of the rows */
138     rowCnt = num_of_rows & 0x1;
139 
140     while (rowCnt)
141     {
142         q31_t     sum = ((q31_t)(*pBias++) << bias_shift) + NN_ROUND(out_shift);
143         uint16_t  colCnt = dim_vec >> 2;
144 
145         pA = pV;
146 
147         while (colCnt)
148         {
149             q31_t     inV1, inV2, inM11, inM12;
150 
151             pB = (q7_t *) read_and_pad((void *)pB, &inM11, &inM12);
152 
153             inV1 = *__SIMD32(pA)++;
154             sum = __SMLAD(inV1, inM11, sum);
155 
156             inV2 = *__SIMD32(pA)++;
157             sum = __SMLAD(inV2, inM12, sum);
158 
159             colCnt--;
160         }
161 
162         /* left-over of the vector */
163         colCnt = dim_vec & 0x3;
164         while (colCnt)
165         {
166             q15_t     inV = *pA++;
167             q7_t      inM = *pB++;
168             sum += inV * inM;
169             colCnt--;
170         }
171 
172         *pO++ = (q15_t) (__SSAT((sum >> out_shift), 16));
173 
174         rowCnt--;
175     }
176 
177 #else
178     int       i, j;
179     /* Run the following code as reference implementation for Cortex-M0 and Cortex-M3 */
180     for (i = 0; i < num_of_rows; i++)
181     {
182         int       ip_out = ((q31_t)(bias[i]) << bias_shift) + NN_ROUND(out_shift);
183         for (j = 0; j < dim_vec; j++)
184         {
185             ip_out += pV[j] * pM[i * dim_vec + j];
186         }
187         pOut[i] = (q15_t) __SSAT((ip_out >> out_shift), 16);
188     }
189 
190 #endif                          /* ARM_MATH_DSP */
191 
192     /* Return to ARM_MATH_SUCCESS */
193     return (ARM_MATH_SUCCESS);
194 
195 }
196 
197 /**
198  * @} end of FC group
199  */
200