altos: Unroll viterbi state loop for >30% performance boost
[fw/altos] / src / core / ao_fec_rx.c
1 /*
2  * Copyright © 2012 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; version 2 of the License.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
16  */
17
18 #include <ao_fec.h>
19 #include <stdio.h>
20
21 #ifdef MEGAMETRUM
22 #include <ao.h>
23 #endif
24
25 #if AO_PROFILE
26 #include <ao_profile.h>
27
28 uint32_t        ao_fec_decode_start, ao_fec_decode_end;
29 #endif
30
31 /* 
32  * byte order repeats through 3 2 1 0
33  *      
34  * bit-pair order repeats through
35  *
36  *  1/0 3/2 5/4 7/6
37  *
38  * So, the over all order is:
39  *
40  *      3,1/0   2,1/0   1,1/0   0,1/0
41  *      3,3/2   2,3/2   1,3/2   0,3/2
42  *      3,5/4   2,5/4   1,5/4   0,5/4
43  *      3,7/6   2,7/6   1,7/6   0,7/6
44  *
45  * The raw bit order is thus
46  *
47  *      1e/1f   16/17   0e/0f   06/07
48  *      1c/1d   14/15   0c/0d   04/05
49  *      1a/1b   12/13   0a/0b   02/03
50  *      18/19   10/11   08/09   00/01
51  */
52
53 static const uint8_t ao_interleave_order[] = {
54         0x1e, 0x16, 0x0e, 0x06,
55         0x1c, 0x14, 0x0c, 0x04,
56         0x1a, 0x12, 0x0a, 0x02,
57         0x18, 0x10, 0x08, 0x00
58 };
59
60 static inline uint16_t ao_interleave_index(uint16_t i) {
61         return (i & ~0x1e) | ao_interleave_order[(i & 0x1e) >> 1];
62 }
63
64 #define NUM_STATE       8
65 #define NUM_HIST        8
66
67 #define V_0             0xff
68 #define V_1             0x00
69
70 /*
71  * These are just the 'zero' states; the 'one' states mirror them
72  */
73 static const uint8_t ao_fec_decode_table[NUM_STATE*2] = {
74         V_0, V_0,       /* 000 */
75         V_0, V_1,       /* 001 */
76         V_1, V_1,       /* 010 */
77         V_1, V_0,       /* 011 */
78         V_1, V_1,       /* 100 */
79         V_1, V_0,       /* 101 */
80         V_0, V_0,       /* 110 */
81         V_0, V_1        /* 111 */
82 };
83
84 static inline uint8_t
85 ao_next_state(uint8_t state, uint8_t bit)
86 {
87         return ((state << 1) | bit) & 0x7;
88 }
89
90 /*
91  * 'in' is 8-bits per symbol soft decision data
92  * 'len' is input byte length. 'out' must be
93  * 'len'/16 bytes long
94  */
95
96 uint8_t
97 ao_fec_decode(const uint8_t *in, uint16_t len, uint8_t *out, uint8_t out_len, uint16_t (*callback)())
98 {
99         static uint32_t cost[2][NUM_STATE];             /* path cost */
100         static uint16_t bits[2][NUM_STATE];             /* save bits to quickly output them */
101
102         uint16_t        i;                              /* input byte index */
103         uint16_t        b;                              /* encoded symbol index (bytes/2) */
104         uint16_t        o;                              /* output bit index */
105         uint8_t         p;                              /* previous cost/bits index */
106         uint8_t         n;                              /* next cost/bits index */
107         uint8_t         state;                          /* state index */
108         const uint8_t   *whiten = ao_fec_whiten_table;
109         uint16_t        interleave;                     /* input byte array index */
110         uint8_t         s0, s1;
111         uint16_t        avail;
112         uint16_t        crc = AO_FEC_CRC_INIT;
113 #if AO_PROFILE
114         uint32_t        start_tick;
115 #endif
116
117         p = 0;
118         for (state = 0; state < NUM_STATE; state++) {
119                 cost[0][state] = 0x7fffffff;
120                 bits[0][state] = 0;
121         }
122         cost[0][0] = 0;
123
124         if (callback)
125                 avail = 0;
126         else
127                 avail = len;
128
129 #if AO_PROFILE
130         if (!avail) {
131                 avail = callback();
132                 if (!avail)
133                         return 0;
134         }
135         start_tick = ao_profile_tick();
136 #endif
137         o = 0;
138         for (i = 0; i < len; i += 2) {
139                 b = i/2;
140                 n = p ^ 1;
141
142                 if (!avail) {
143                         avail = callback();
144                         if (!avail)
145                                 return 0;
146                 }
147
148                 /* Fetch one pair of input bytes, de-interleaving
149                  * the input.
150                  */
151                 interleave = ao_interleave_index(i);
152                 s0 = in[interleave];
153                 s1 = in[interleave+1];
154
155                 avail -= 2;
156
157                 /* Reset next costs to 'impossibly high' values so that
158                  * the first path through this state is cheaper than this
159                  */
160                 for (state = 0; state < NUM_STATE; state++)
161                         cost[n][state] = 0x7fffffff;
162
163                 /* Compute path costs and accumulate output bit path
164                  * for each state and encoded bit value. Unrolling
165                  * this loop is worth about > 30% performance boost.
166                  * Decoding 76-byte remote access packets is reduced
167                  * from 14.700ms to 9.3ms
168                  */
169 #define DO_STATE(state) {                                               \
170                         uint32_t        bitcost = ((uint32_t) (s0 ^ ao_fec_decode_table[(state<<1)]) + \
171                                                    (uint32_t) (s1 ^ ao_fec_decode_table[(state<<1)+1])); \
172                         {                                               \
173                                 uint32_t        cost0 = cost[p][state] + bitcost; \
174                                 uint8_t         state0 = ao_next_state(state, 0); \
175                                                                         \
176                                 if (cost0 < cost[n][state0]) {          \
177                                         cost[n][state0] = cost0;        \
178                                         bits[n][state0] = (bits[p][state] << 1) | (state & 1); \
179                                 }                                       \
180                         }                                               \
181                         {                                               \
182                                 uint32_t        cost1 = cost[p][state] + 510 - bitcost; \
183                                 uint8_t         state1 = ao_next_state(state, 1); \
184                                                                         \
185                                 if (cost1 < cost[n][state1]) {          \
186                                         cost[n][state1] = cost1;        \
187                                         bits[n][state1] = (bits[p][state] << 1) | (state & 1); \
188                                 }                                       \
189                         }                                               \
190                 }
191
192                 DO_STATE(0);
193                 DO_STATE(1);
194                 DO_STATE(2);
195                 DO_STATE(3);
196                 DO_STATE(4);
197                 DO_STATE(5);
198                 DO_STATE(6);
199                 DO_STATE(7);
200
201 #if 0
202                 printf ("bit %3d symbol %2x %2x:", i/2, s0, s1);
203                 for (state = 0; state < NUM_STATE; state++) {
204                         printf (" %5d(%04x)", cost[n][state], bits[n][state]);
205                 }
206                 printf ("\n");
207 #endif
208                 p = n;
209
210                 /* A loop is needed to handle the last output byte. It
211                  * won't have any bits of future data to perform full
212                  * error correction, but we might as well give the
213                  * best possible answer anyways.
214                  */
215                 while ((b - o) >= (8 + NUM_HIST) || (i + 2 >= len && b > o)) {
216
217                         /* Compute number of bits to the end of the
218                          * last full byte of data. This is generally
219                          * NUM_HIST, unless we've reached
220                          * the end of the input, in which case
221                          * it will be seven.
222                          */
223                         int8_t          dist = b - (o + 8);     /* distance to last ready-for-writing bit */
224                         uint32_t        min_cost;               /* lowest cost */
225                         uint8_t         min_state;              /* lowest cost state */
226                         uint8_t         byte;
227
228                         /* Find the best fit at the current point
229                          * of the decode.
230                          */
231                         min_cost = cost[p][0];
232                         min_state = 0;
233                         for (state = 1; state < NUM_STATE; state++) {
234                                 if (cost[p][state] < min_cost) {
235                                         min_cost = cost[p][state];
236                                         min_state = state;
237                                 }
238                         }
239
240                         /* The very last byte of data has the very last bit
241                          * of data left in the state value; just smash the
242                          * bits value in place and reset the 'dist' from
243                          * -1 to 0 so that the full byte is read out
244                          */
245                         if (dist < 0) {
246                                 bits[p][min_state] = (bits[p][min_state] << 1) | (min_state & 1);
247                                 dist = 0;
248                         }
249
250 #if 0
251                         printf ("\tbit %3d min_cost %5d old bit %3d old_state %x bits %02x whiten %0x\n",
252                                 i/2, min_cost, o + 8, min_state, (bits[p][min_state] >> dist) & 0xff, *whiten);
253 #endif
254                         byte = (bits[p][min_state] >> dist) ^ *whiten++;
255                         *out++ = byte;
256                         if (out_len > 2)
257                                 crc = ao_fec_crc_byte(byte, crc);
258
259                         if (!--out_len) {
260                                 if ((out[-2] == (uint8_t) (crc >> 8)) &&
261                                     out[-1] == (uint8_t) crc)
262                                         out[-1] = AO_FEC_DECODE_CRC_OK;
263                                 else
264                                         out[-1] = 0;
265                                 out[-2] = 0;
266                                 goto done;
267                         }
268                         o += 8;
269                 }
270         }
271 done:
272 #if AO_PROFILE
273         ao_fec_decode_start = start_tick;
274         ao_fec_decode_end = ao_profile_tick();
275 #endif
276         return 1;
277 }