1 /*
2  * Copyright (C) 2010-2021 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_q7.c
22  * Description:  Q7 basic fully-connected layer function
23  *
24  * $Date:        January 26, 2021
25  * $Revision:    V.1.0.2
26  *
27  * Target Processor:  Cortex-M cores
28  *
29  * -------------------------------------------------------------------- */
30 
31 #include "arm_nnfunctions.h"
32 #include "arm_nnsupportfunctions.h"
33 
34 /**
35  *  @ingroup groupNN
36  */
37 
38 /**
39  * @addtogroup FC
40  * @{
41  */
42 
43 /**
44  * @brief Q7 basic 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: dim_vec
61  *
62  * This basic function is designed to work with regular weight
63  * matrix without interleaving.
64  *
65  */
66 
arm_fully_connected_q7(const q7_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,q7_t * pOut,q15_t * vec_buffer)67 arm_status arm_fully_connected_q7(const q7_t *pV,
68                                   const q7_t *pM,
69                                   const uint16_t dim_vec,
70                                   const uint16_t num_of_rows,
71                                   const uint16_t bias_shift,
72                                   const uint16_t out_shift,
73                                   const q7_t *bias,
74                                   q7_t *pOut,
75                                   q15_t *vec_buffer)
76 {
77 
78 #if defined(ARM_MATH_DSP)
79     /* Run the following code for Cortex-M4 and Cortex-M7 */
80 
81     const q7_t *pB = pM;
82     const q7_t *pB2;
83     q7_t *pO = pOut;
84     const q7_t *pBias = bias;
85     const q15_t *pA;
86     uint16_t rowCnt = num_of_rows >> 1;
87 
88     /* expand the vector into the buffer */
89     arm_q7_to_q15_reordered_no_shift(pV, vec_buffer, dim_vec);
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 = vec_buffer;
98         pB2 = pB + dim_vec;
99 
100         while (colCnt)
101         {
102             q31_t inV, inM11, inM12, inM21, inM22;
103             pB = read_and_pad_reordered(pB, &inM11, &inM12);
104             pB2 = read_and_pad_reordered(pB2, &inM21, &inM22);
105 
106             inV = arm_nn_read_q15x2_ia(&pA);
107 
108             sum = __SMLAD(inV, inM11, sum);
109             sum2 = __SMLAD(inV, inM21, sum2);
110 
111             inV = arm_nn_read_q15x2_ia(&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             q7_t inV = *pA++;
122             q15_t inM = *pB++;
123             q15_t inM2 = *pB2++;
124 
125             sum += inV * inM;
126             sum2 += inV * inM2;
127             colCnt--;
128         } /* while over colCnt */
129         *pO++ = (q7_t)(__SSAT((sum >> out_shift), 8));
130         *pO++ = (q7_t)(__SSAT((sum2 >> out_shift), 8));
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         uint16_t colCnt = dim_vec >> 2;
143         q31_t sum = ((q31_t)(*pBias++) << bias_shift) + NN_ROUND(out_shift);
144 
145         pA = vec_buffer;
146 
147         while (colCnt)
148         {
149             q31_t inV1, inV2, inM11, inM12;
150 
151             pB = read_and_pad_reordered(pB, &inM11, &inM12);
152 
153             inV1 = arm_nn_read_q15x2_ia(&pA);
154             sum = __SMLAD(inV1, inM11, sum);
155 
156             inV2 = arm_nn_read_q15x2_ia(&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             q7_t inV = *pA++;
167             q15_t inM = *pB++;
168             sum += inV * inM;
169             colCnt--;
170         }
171 
172         *pO++ = (q7_t)(__SSAT((sum >> out_shift), 8));
173 
174         rowCnt--;
175     }
176 
177 #else
178     (void)vec_buffer;
179     int i, j;
180 
181     /* Run the following code as reference implementation for Cortex-M0 and Cortex-M3 */
182     for (i = 0; i < num_of_rows; i++)
183     {
184         int ip_out = ((q31_t)(bias[i]) << bias_shift) + NN_ROUND(out_shift);
185         for (j = 0; j < dim_vec; j++)
186         {
187             ip_out += pV[j] * pM[i * dim_vec + j];
188         }
189         pOut[i] = (q7_t)__SSAT((ip_out >> out_shift), 8);
190     }
191 
192 #endif /* ARM_MATH_DSP */
193 
194     /* Return to ARM_MATH_SUCCESS */
195     return (ARM_MATH_SUCCESS);
196 }
197 
198 /**
199  * @} end of FC group
200  */
201