1/*-
2 * Copyright (c) 2003
3 *	Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4 * 	All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 * Author: Hartmut Brandt <harti@freebsd.org>
28 *
29 * This program is used to generate the different rate tables for the IDT77252
30 * driver. The generated tables are slightly different from those in the
31 * IDT manual.
32 */
33#include <sys/cdefs.h>
34__FBSDID("$FreeBSD: releng/11.0/sys/dev/patm/genrtab/genrtab.c 139749 2005-01-06 01:43:34Z imp $");
35
36#include <sys/types.h>
37#include <stdio.h>
38#include <unistd.h>
39#include <math.h>
40#include <ieeefp.h>
41
42/* verbosity flag */
43static int verbose;
44
45/* number of table entries */
46static const u_int tsize = 256;
47
48/* number of rate difference tables to create */
49static const u_int ndtables = 16;
50
51/* cell rate offset for log 0 */
52static const double offset = 10.0;
53
54/*
55 * Make an internal form of the interval and be sure to round down.
56 */
57static u_int
58d2interval(double d)
59{
60	fp_rnd_t r;
61	u_int s, id;
62
63	r = fpsetround(FP_RZ);
64	id = (u_int)rint(32 * d);
65	fpsetround(r);
66
67	s = 0;
68	while (id >= 32 * 32) {
69		s++;
70		id >>= 1;
71	}
72	return ((s << 10) | (id));
73}
74
75/*
76 * Convert an internal interval back to a real one.
77 */
78static double
79interval2d(u_int id)
80{
81	return ((1 << ((id >> 10) & 0xf)) * ((id & 0x3ff) / 32.0));
82}
83
84/*
85 * Convert double to ATM-Forum format. Make sure to round up.
86 */
87static u_int
88cps2atmf(double cps)
89{
90	fp_rnd_t r;
91	u_int s, id;
92
93	if (cps < 1.0)
94		return (0);
95
96	s = 0;
97	while (cps >= 2.0) {
98		s++;
99		cps /= 2;
100	}
101	r = fpsetround(FP_RP);
102	id = (u_int)rint(512 * cps);
103	fpsetround(r);
104
105	return ((1 << 14) | (s << 9) | (id & 0x1ff));
106}
107
108/*
109 * Convert ATM forum format to double
110 */
111static double
112atmf2cps(u_int atmf)
113{
114	return (((atmf >> 14) & 1) * (1 << ((atmf >> 9) & 0x1f)) *
115	    ((512 + (atmf & 0x1ff)) / 512.0));
116}
117
118/*
119 * A cell rate to the logarithmic one
120 */
121static double
122cps2log(u_int alink, double lg)
123{
124	if (lg <= offset)
125		return (0);
126	if (lg >= alink)
127		return (tsize - 1);
128
129	return ((tsize - 1) * (1 - log(alink / lg) / log(alink / offset)));
130}
131
132/*
133 * Convert log to cell rate
134 */
135static double
136log2cps(u_int alink, u_int lg)
137{
138	return (alink / pow(alink / offset,
139	    (double)(tsize - lg - 1) / (tsize - 1)));
140}
141
142/*
143 * Convert a double to an internal scaled double
144 */
145static u_int
146d2ifp(double fp)
147{
148	fp_rnd_t r;
149	u_int s, ifp;
150
151	fp *= (1 << 16);
152
153	r = fpsetround(FP_RN);
154	ifp = (u_int)rint(fp);
155	fpsetround(r);
156
157	s = 0;
158	while (ifp >= 1024) {
159		s++;
160		ifp >>= 1;
161	}
162	return ((s << 10) | (ifp));
163}
164
165/*
166 * Convert internal scaled float to double
167 */
168static double
169ifp2d(u_int p)
170{
171	return ((p & 0x3ff) * (1 << ((p >> 10) & 0xf)) / 65536.0);
172}
173
174/*
175 * Generate log to rate conversion table
176 */
177static void
178gen_log2rate(u_int alink)
179{
180	u_int i, iinterval, atmf, n, nrm;
181	double rate, interval, xinterval, cps, xcps;
182
183	for (i = 0; i < 256; i++) {
184		/* get the desired rate */
185		rate = alink / pow(alink / offset,
186		    (double)(tsize - i - 1) / (tsize - 1));
187
188		/* convert this to an interval */
189		interval = alink / rate;
190
191		/* make the internal form of this interval, be sure to
192		 * round down */
193		iinterval = d2interval(interval);
194
195		/* now convert back */
196		xinterval = interval2d(iinterval);
197
198		/* make a cps from this interval */
199		cps = alink / xinterval;
200
201		/* convert this to its ATM forum format */
202		atmf = cps2atmf(cps);
203
204		/* and back */
205		xcps = atmf2cps(atmf);
206
207		/* decide on NRM */
208		if (xcps < 40.0) {
209			nrm = 0;
210			n = 3;
211		} else if (xcps < 80.0) {
212			nrm = 1;
213			n = 4;
214		} else if (xcps < 160.0) {
215			nrm = 2;
216			n = 8;
217		} else if (xcps < 320.0) {
218			nrm = 3;
219			n = 16;
220		} else {
221			nrm = 4;
222			n = 32;
223		}
224
225		/* print */
226		if (verbose)
227			printf(" 0x%08x, /* %03u: cps=%f nrm=%u int=%f */\n",
228			    (atmf << 17) | (nrm << 14) | iinterval, i,
229			    xcps, n, xinterval);
230		else
231			printf("0x%08x,\n", (atmf << 17) | (nrm << 14) |
232			    iinterval);
233	}
234}
235
236/*
237 * Generate rate to log conversion table
238 */
239static void
240gen_rate2log(u_int alink)
241{
242	u_int i, atmf, val, ilcr;
243	double cps, lcr;
244	fp_rnd_t r;
245
246	val = 0;
247	for (i = 0; i < 512; i++) {
248		/* make ATM Forum CPS from index */
249		atmf = (((i & 0x1f0) >> 4) << 9) |
250		    ((i & 0xf) << 5) | (1 << 14);
251
252		/* make cps */
253		cps = atmf2cps(atmf);
254
255		/* convert to log */
256		lcr = cps2log(alink, cps);
257
258		r = fpsetround(FP_RN);
259		ilcr = (u_int)rint(lcr);
260		fpsetround(r);
261
262		/* put together */
263		val |= ilcr << (8 * (i % 4));
264
265		/* print */
266		if (i % 4 == 3) {
267			if (verbose)
268				printf(" 0x%08x,\t", val);
269			else
270				printf("0x%08x,\n", val);
271			val = 0;
272		} else if (verbose)
273			printf("\t\t");
274		if (verbose)
275			printf("/* %03u: %f -> %f */\n", i,
276			    cps, log2cps(alink, ilcr));
277	}
278}
279
280/*
281 * Generate one entry into the global table
282 */
283static void
284gen_glob_entry(u_int alink, u_int fill, u_int ci, u_int ni)
285{
286	if (verbose)
287		printf("  0x%08x,	/* %2u/32 %8.6f, %6u, ci=%u, ni=%u */\n",
288		    cps2atmf(alink * fill / 32.0) | (ci << 17) | (ni << 16),
289		    fill, fill / 32.0, alink * fill / 32, ci, ni);
290	else
291		printf("0x%08x,\n",
292		    cps2atmf(alink * fill / 32.0) | (ci << 17) | (ni << 16));
293}
294
295/*
296 * Generate global parameter table
297 */
298static void
299gen_glob(u_int alink)
300{
301	u_int i;
302
303	gen_glob_entry(alink, 32, 0, 0);
304	gen_glob_entry(alink, 16, 0, 0);
305	gen_glob_entry(alink,  8, 0, 1);
306	gen_glob_entry(alink,  4, 0, 1);
307	gen_glob_entry(alink,  2, 1, 1);
308	gen_glob_entry(alink,  1, 1, 1);
309	gen_glob_entry(alink,  0, 1, 1);
310	gen_glob_entry(alink,  0, 1, 1);
311
312	for (i = 0; i < tsize/2 - 8; i++) {
313		if (i % 16 == 0)
314			printf(" ");
315		printf(" 0,");
316		if (i % 16 == 15)
317			printf("\n");
318	}
319	printf("\n");
320}
321
322/*
323 * Generate additive rate increase tables
324 */
325static void
326gen_air(u_int alink)
327{
328	u_int t, i;
329	double diff;	/* cell rate to increase by */
330	double cps;
331	double add;
332	u_int val, a;
333
334	for (t = 0; t < ndtables; t++) {
335		diff = (double)alink / (1 << t);
336		printf("/* AIR %u: diff=%f */\n", t, diff);
337		val = 0;
338		for (i = 0; i < tsize; i++) {
339			cps = log2cps(alink, i);
340			cps += diff;
341			if (cps > alink)
342				cps = alink;
343
344			add = cps2log(alink, cps) - i;
345
346			a = d2ifp(add);
347
348			if (i % 2) {
349				val |= a << 16;
350				if (verbose)
351					printf("  0x%08x,\t", val);
352				else
353					printf("0x%08x,\n", val);
354			} else {
355				val = a;
356				if (verbose)
357					printf("\t\t");
358			}
359			if (verbose)
360				printf("/* %3u: %f */\n", i, ifp2d(add));
361		}
362	}
363}
364
365/*
366 * Generate rate decrease table
367 */
368static void
369gen_rdf(u_int alink)
370{
371	double d;
372	u_int t, i, f, val, diff;
373
374	for (t = 0; t < ndtables; t++) {
375		/* compute the log index difference */
376		if (t == 0) {
377			d = tsize - 1;
378		} else {
379			f = 1 << t;
380			d = (tsize - 1) / log(alink / offset);
381			d *= log((double)f / (f - 1));
382		}
383		printf(" /* RDF %u: 1/%u: %f */\n", t, 1 << t, d);
384		val = 0;
385		for (i = 0; i < tsize; i++) {
386			if (i < d)
387				diff = d2ifp(i);
388			else
389				diff = d2ifp(d);
390			if (i % 2) {
391				val |= diff << 16;
392				if (verbose)
393					printf("  0x%08x,\t", val);
394				else
395					printf("0x%08x,\n", val);
396			} else {
397				val = diff;
398				if (verbose)
399					printf("\t\t");
400			}
401			if (verbose)
402				printf("/* %3u: %f */\n", i, ifp2d(diff));
403		}
404	}
405}
406
407/*
408 * Create all the tables for a given link cell rate and link bit rate.
409 * The link bit rate is only used to name the table.
410 */
411static void
412gen_tables(u_int alink, u_int mbps)
413{
414	printf("\n");
415	printf("/*\n");
416	printf(" * Tables for %ucps and %uMbps\n", alink, mbps);
417	printf(" */\n");
418	printf("const uint32_t patm_rtables%u[128 * (4 + 2 * %u)] = {\n",
419	    mbps, ndtables);
420
421	gen_log2rate(alink);
422	gen_rate2log(alink);
423	gen_glob(alink);
424	gen_air(alink);
425	gen_rdf(alink);
426
427	printf("};\n");
428}
429
430int
431main(int argc, char *argv[])
432{
433	int opt;
434
435	while ((opt = getopt(argc, argv, "v")) != -1)
436		switch (opt) {
437
438		  case 'v':
439			verbose = 1;
440			break;
441		}
442
443	printf("/*\n");
444	printf(" * This file was generated by `%s'\n", argv[0]);
445	printf(" */\n");
446	printf("\n");
447	printf("#include <sys/cdefs.h>\n");
448	printf("__FBSDID(\"$FreeBSD: releng/11.0/sys/dev/patm/genrtab/genrtab.c 139749 2005-01-06 01:43:34Z imp $\");\n");
449	printf("\n");
450	printf("#include <sys/types.h>\n");
451	printf("\n");
452	printf("const u_int patm_rtables_size = 128 * (4 + 2 * %u);\n",
453	    ndtables);
454	printf("const u_int patm_rtables_ntab = %u;\n", ndtables);
455	gen_tables(352768, 155);
456	gen_tables( 59259,  25);
457	return (0);
458}
459