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