xref: /freebsd/sys/dev/bhnd/tools/nvram_map_gen.awk (revision 8ef24a0d4b28fe230e20637f56869cc4148cd2ca)
1#!/usr/bin/awk -f
2
3#-
4# Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
5# All rights reserved.
6#
7# Redistribution and use in source and binary forms, with or without
8# modification, are permitted provided that the following conditions
9# are met:
10# 1. Redistributions of source code must retain the above copyright
11#    notice, this list of conditions and the following disclaimer,
12#    without modification.
13# 2. Redistributions in binary form must reproduce at minimum a disclaimer
14#    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
15#    redistribution must be conditioned upon including a substantially
16#    similar Disclaimer requirement for further binary redistribution.
17#
18# NO WARRANTY
19# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21# LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
22# AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
23# THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
24# OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
27# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
29# THE POSSIBILITY OF SUCH DAMAGES.
30#
31# $FreeBSD$
32
33BEGIN {
34	RS="\n"
35
36	depth = 0
37	symbols[depth,"_file"] = FILENAME
38	num_output_vars = 0
39	OUTPUT_FILE = null
40
41	# Seed rand()
42	srand()
43
44	# Output type
45	OUT_T = null
46	OUT_T_HEADER = "HEADER"
47	OUT_T_DATA = "DATA"
48
49	# Enable debug output
50	DEBUG = 0
51
52	# Maximum revision
53	REV_MAX = 255
54
55	# Parse arguments
56	if (ARGC < 2)
57		usage()
58
59	for (i = 1; i < ARGC; i++) {
60		if (ARGV[i] == "--debug") {
61			DEBUG = 1
62		} else if (ARGV[i] == "-d" && OUT_T == null) {
63			OUT_T = OUT_T_DATA
64		} else if (ARGV[i] == "-h" && OUT_T == null) {
65			OUT_T = OUT_T_HEADER
66		} else if (ARGV[i] == "-o") {
67			i++
68			if (i >= ARGC)
69				usage()
70
71			OUTPUT_FILE = ARGV[i]
72		} else if (ARGV[i] == "--") {
73			i++
74			break
75		} else if (ARGV[i] !~ /^-/) {
76			FILENAME = ARGV[i]
77		} else {
78			print "unknown option " ARGV[i]
79			usage()
80		}
81	}
82
83	ARGC=2
84
85	if (OUT_T == null) {
86		print("error: one of -d or -h required")
87		usage()
88	}
89
90	if (FILENAME == null) {
91		print("error: no input file specified")
92		usage()
93	}
94
95	if (OUTPUT_FILE == "-") {
96		OUTPUT_FILE = "/dev/stdout"
97	} else if (OUTPUT_FILE == null) {
98		_bi = split(FILENAME, _paths, "/")
99		OUTPUT_FILE = _paths[_bi]
100
101		if (OUTPUT_FILE !~ /^bhnd_/)
102			OUTPUT_FILE = "bhnd_" OUTPUT_FILE
103
104		if (OUT_T == OUT_T_HEADER)
105			OUTPUT_FILE = OUTPUT_FILE ".h"
106		else
107			OUTPUT_FILE = OUTPUT_FILE "_data.h"
108	}
109
110	# Format Constants
111	FMT["hex"]	= "BHND_NVRAM_VFMT_HEX"
112	FMT["decimal"]	= "BHND_NVRAM_VFMT_DEC"
113	FMT["ccode"]	= "BHND_NVRAM_VFMT_CCODE"
114	FMT["macaddr"]	= "BHND_NVRAM_VFMT_MACADDR"
115	FMT["led_dc"]	= "BHND_NVRAM_VFMT_LEDDC"
116
117	# Data Type Constants
118	DTYPE["u8"]	= "BHND_NVRAM_DT_UINT8"
119	DTYPE["u16"]	= "BHND_NVRAM_DT_UINT16"
120	DTYPE["u32"]	= "BHND_NVRAM_DT_UINT32"
121	DTYPE["i8"]	= "BHND_NVRAM_DT_INT8"
122	DTYPE["i16"]	= "BHND_NVRAM_DT_INT16"
123	DTYPE["i32"]	= "BHND_NVRAM_DT_INT32"
124	DTYPE["char"]	= "BHND_NVRAM_DT_CHAR"
125
126	# Default masking for standard types
127	TMASK["u8"]	= "0x000000FF"
128	TMASK["u16"]	= "0x0000FFFF"
129	TMASK["u32"]	= "0xFFFFFFFF"
130	TMASK["i8"]	= TMASK["u8"]
131	TMASK["i16"]	= TMASK["u16"]
132	TMASK["i32"]	= TMASK["u32"]
133	TMASK["char"]	= TMASK["u8"]
134
135	# Byte sizes for standard types
136	TSIZE["u8"]	= "1"
137	TSIZE["u16"]	= "2"
138	TSIZE["u32"]	= "4"
139	TSIZE["i8"]	= TSIZE["u8"]
140	TSIZE["i16"]	= TSIZE["u8"]
141	TSIZE["i32"]	= TSIZE["u8"]
142	TSIZE["char"]	= "1"
143
144	# Common Regexs
145	INT_REGEX	= "^(0|[1-9][0-9]*),?$"
146	HEX_REGEX	= "^0x[A-Fa-f0-9]+,?$"
147
148	ARRAY_REGEX	= "\\[(0|[1-9][0-9]*)\\]"
149	TYPES_REGEX	= "^(((u|i)(8|16|32))|char)("ARRAY_REGEX")?,?$"
150
151	IDENT_REGEX	= "^[A-Za-z_][A-Za-z0-9_]*,?$"
152	SROM_OFF_REGEX	= "("TYPES_REGEX"|"HEX_REGEX")"
153
154	# Parser states types
155	ST_STRUCT_BLOCK	= "struct"	# struct block
156	ST_VAR_BLOCK	= "var"		# variable block
157	ST_SROM_DEFN	= "srom"	# srom offset defn
158	ST_NONE		= "NONE"	# default state
159
160	# Property types
161	PROP_T_SFMT	= "sfmt"
162	PROP_T_ALL1	= "all1"
163
164	# Internal variables used for parser state
165	# tracking
166	STATE_TYPE	= "_state_type"
167	STATE_IDENT	= "_state_block_name"
168	STATE_LINENO	= "_state_first_line"
169	STATE_ISBLOCK	= "_state_is_block"
170
171	# Common array keys
172	DEF_LINE	= "def_line"
173	NUM_REVS	= "num_revs"
174	REV		= "rev"
175
176	# Revision array keys
177	REV_START	= "rev_start"
178	REV_END		= "rev_end"
179	REV_DESC	= "rev_decl"
180	REV_NUM_OFFS	= "num_offs"
181
182	# Offset array keys
183	OFF 		= "off"
184	OFF_NUM_SEGS	= "off_num_segs"
185	OFF_SEG		= "off_seg"
186
187	# Segment array keys
188	SEG_ADDR	= "seg_addr"
189	SEG_COUNT	= "seg_count"
190	SEG_TYPE	= "seg_type"
191	SEG_MASK	= "seg_mask"
192	SEG_SHIFT	= "seg_shift"
193
194	# Variable array keys
195	VAR_NAME	= "v_name"
196	VAR_TYPE	= "v_type"
197	VAR_BASE_TYPE	= "v_base_type"
198	VAR_FMT		= "v_fmt"
199	VAR_STRUCT	= "v_parent_struct"
200	VAR_PRIVATE	= "v_private"
201	VAR_ARRAY	= "v_array"
202	VAR_IGNALL1	= "v_ignall1"
203}
204
205# return the flag definition for variable `v`
206function gen_var_flags (v)
207{
208	_num_flags = 0;
209	if (vars[v,VAR_ARRAY])
210		_flags[_num_flags++] = "BHND_NVRAM_VF_ARRAY"
211
212	if (vars[v,VAR_PRIVATE])
213		_flags[_num_flags++] = "BHND_NVRAM_VF_MFGINT"
214
215	if (vars[v,VAR_IGNALL1])
216		_flags[_num_flags++] = "BHND_NVRAM_VF_IGNALL1"
217
218	if (_num_flags == 0)
219		_flags[_num_flags++] = "0"
220
221	return (join(_flags, "|", _num_flags))
222}
223
224# emit the bhnd_sprom_offsets for a given variable revision key
225function emit_var_sprom_offsets (v, revk)
226{
227	emit(sprintf("{{%u, %u}, (struct bhnd_sprom_offset[]) {\n",
228	    vars[revk,REV_START],
229	    vars[revk,REV_END]))
230	output_depth++
231
232	num_offs = vars[revk,REV_NUM_OFFS]
233	num_offs_written = 0
234	elem_count = 0
235	for (offset = 0; offset < num_offs; offset++) {
236		offk = subkey(revk, OFF, offset"")
237		num_segs = vars[offk,OFF_NUM_SEGS]
238
239		for (seg = 0; seg < num_segs; seg++) {
240			segk = subkey(offk, OFF_SEG, seg"")
241
242			for (seg_n = 0; seg_n < vars[segk,SEG_COUNT]; seg_n++) {
243				seg_addr = vars[segk,SEG_ADDR]
244				seg_addr += TSIZE[vars[segk,SEG_TYPE]] * seg_n
245
246				emit(sprintf("{%s, %s, %s, %s, %s},\n",
247				    seg_addr,
248				    (seg > 0) ? "true" : "false",
249				    DTYPE[vars[segk,SEG_TYPE]],
250				    vars[segk,SEG_SHIFT],
251				    vars[segk,SEG_MASK]))
252
253				num_offs_written++
254			}
255		}
256	}
257
258	output_depth--
259	emit("}, " num_offs_written "},\n")
260}
261
262# emit the bhnd_nvram_var definition for variable name `v`
263function emit_var_defn (v)
264{
265	emit(sprintf("{\"%s\", %s, %s, %s, (struct bhnd_sprom_var[]) {\n",
266		    v suffix,
267		    DTYPE[vars[v,VAR_BASE_TYPE]],
268		    FMT[vars[v,VAR_FMT]],
269		    gen_var_flags(v)))
270	output_depth++
271
272	for (rev = 0; rev < vars[v,NUM_REVS]; rev++) {
273		revk = subkey(v, REV, rev"")
274		emit_var_sprom_offsets(v, revk)
275	}
276
277	output_depth--
278	emit("}, " vars[v,NUM_REVS] "},\n")
279}
280
281# emit a header name #define for variable `v`
282function emit_var_namedef (v)
283{
284	emit("#define\tBHND_NVAR_" toupper(v) "\t\"" v "\"\n")
285}
286
287# generate a set of var offset definitions for struct variable `st_vid`
288function gen_struct_var_offsets (vid, revk, st_vid, st_revk, base_addr)
289{
290	# Copy all offsets to the new variable
291	for (offset = 0; offset < vars[v,REV_NUM_OFFS]; offset++) {
292		st_offk = subkey(st_revk, OFF, offset"")
293		offk = subkey(revk, OFF, offset"")
294
295		# Copy all segments to the new variable, applying base
296		# address adjustment
297		num_segs = vars[st_offk,OFF_NUM_SEGS]
298		vars[offk,OFF_NUM_SEGS] = num_segs
299
300		for (seg = 0; seg < num_segs; seg++) {
301			st_segk = subkey(st_offk, OFF_SEG, seg"")
302			segk = subkey(offk, OFF_SEG, seg"")
303
304			vars[segk,SEG_ADDR]	= vars[st_segk,SEG_ADDR] + \
305			    base_addr""
306			vars[segk,SEG_COUNT]	= vars[st_segk,SEG_COUNT]
307			vars[segk,SEG_TYPE]	= vars[st_segk,SEG_TYPE]
308			vars[segk,SEG_MASK]	= vars[st_segk,SEG_MASK]
309			vars[segk,SEG_SHIFT]	= vars[st_segk,SEG_SHIFT]
310		}
311	}
312}
313
314# generate a complete set of variable definitions for struct variable `st_vid`.
315function gen_struct_vars (st_vid)
316{
317	st = vars[st_vid,VAR_STRUCT]
318	st_max_off = 0
319
320	# determine the total number of variables to generate
321	for (st_rev = 0; st_rev < structs[st,NUM_REVS]; st_rev++) {
322		srevk = subkey(st, REV, st_rev"")
323		for (off = 0; off < structs[srevk,REV_NUM_OFFS]; off++) {
324			if (off > st_max_off)
325				st_max_off = off
326		}
327	}
328
329	# generate variable records for each defined struct offset
330	for (off = 0; off < st_max_off; off++) {
331		# Construct basic variable definition
332		v = st_vid off""
333		vars[v,VAR_TYPE]	= vars[st_vid,VAR_TYPE]
334		vars[v,VAR_BASE_TYPE]	= vars[st_vid,VAR_BASE_TYPE]
335		vars[v,VAR_FMT]		= vars[st_vid,VAR_FMT]
336		vars[v,VAR_PRIVATE]	= vars[st_vid,VAR_PRIVATE]
337		vars[v,VAR_ARRAY]	= vars[st_vid,VAR_ARRAY]
338		vars[v,VAR_IGNALL1]	= vars[st_vid,VAR_IGNALL1]
339		vars[v,NUM_REVS]	= 0
340
341		# Add to output variable list
342		output_vars[num_output_vars++] = v
343
344		# Construct revision / offset entries
345		for (srev = 0; srev < structs[st,NUM_REVS]; srev++) {
346			# Struct revision key
347			st_revk = subkey(st, REV, srev"")
348
349			# Skip offsets not defined for this revision
350			if (off > structs[st_revk,REV_NUM_OFFS])
351				continue
352
353			# Strut offset key and associated base address */
354			offk = subkey(st_revk, OFF, off"")
355			base_addr = structs[offk,SEG_ADDR]
356
357			for (vrev = 0; vrev < vars[st_vid,NUM_REVS]; vrev++) {
358				st_var_revk = subkey(st_vid, REV, vrev"")
359				v_start	= vars[st_var_revk,REV_START]
360				v_end	= vars[st_var_revk,REV_END]
361				s_start	= structs[st_revk,REV_START]
362				s_end	= structs[st_revk,REV_END]
363
364				# We don't support computing the union
365				# of partially overlapping ranges
366				if ((v_start < s_start && v_end >= s_start) ||
367				    (v_start <= s_end && v_end > s_end))
368				{
369					errorx("partially overlapping " \
370					    "revision ranges are not supported")
371				}
372
373				# skip variables revs that are not within
374				# the struct offset's compatibility range
375				if (v_start < s_start || v_start > s_end ||
376				    v_end < s_start || v_end > s_end)
377					continue
378
379				# Generate the new revision record
380				rev = vars[v,NUM_REVS] ""
381				revk = subkey(v, REV, rev)
382				vars[v,NUM_REVS]++
383
384				vars[revk,DEF_LINE]	= vars[st_revk,DEF_LINE]
385				vars[revk,REV_START]	= v_start
386				vars[revk,REV_END]	= v_end
387				vars[revk,REV_NUM_OFFS] = \
388				    vars[st_var_revk,REV_NUM_OFFS]
389
390				gen_struct_var_offsets(v, revk, st_vid, st_revk,
391				    base_addr)
392			}
393		}
394	}
395}
396
397
398END {
399	# Skip completion handling if exiting from an error
400	if (_EARLY_EXIT)
401		exit 1
402
403	# Check for complete block closure
404	if (depth > 0) {
405		block_start = g(STATE_LINENO)
406		errorx("missing '}' for block opened on line " block_start "")
407	}
408
409	# Generate concrete variable definitions for all struct variables
410	for (v in var_names) {
411		if (vars[v,VAR_STRUCT] != null) {
412			gen_struct_vars(v)
413		} else {
414			output_vars[num_output_vars++] = v
415		}
416	}
417
418	# Apply lexicographical sorting. To support more effecient table
419	# searching, we guarantee a stable sort order (using C collation).
420	sort(output_vars)
421
422	# Truncate output file and write common header
423	printf("") > OUTPUT_FILE
424	emit("/*\n")
425	emit(" * THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT.\n")
426	emit(" *\n")
427	emit(" * generated from nvram map: " FILENAME "\n")
428	emit(" */\n")
429	emit("\n")
430
431	# Emit all variable definitions
432	if (OUT_T == OUT_T_DATA) {
433		emit("#include <dev/bhnd/nvram/nvramvar.h>\n")
434		emit("static const struct bhnd_nvram_var bhnd_nvram_vars[] = "\
435		    "{\n")
436		output_depth++
437		for (i = 0; i < num_output_vars; i++)
438			emit_var_defn(output_vars[i])
439		output_depth--
440		emit("};\n")
441	} else if (OUT_T == OUT_T_HEADER) {
442		for (i = 0; i < num_output_vars; i++)
443			emit_var_namedef(output_vars[i])
444	}
445
446	printf("%u variable records written to %s\n", num_output_vars,
447	    OUTPUT_FILE) >> "/dev/stderr"
448}
449
450
451#
452# Print usage
453#
454function usage ()
455{
456	print "usage: bhnd_nvram_map.awk <input map> [-hd] [-o output file]"
457	_EARLY_EXIT = 1
458	exit 1
459}
460
461#
462# Join all array elements with the given separator
463#
464function join (array, sep, count)
465{
466	if (count == 0)
467		return ("")
468
469	_result = array[0]
470	for (_ji = 1; _ji < count; _ji++)
471		_result = _result sep array[_ji]
472
473	return (_result)
474}
475
476#
477# Sort a contiguous integer-indexed array, using standard awk comparison
478# operators over its values.
479#
480function sort (array) {
481	# determine array size
482	_sort_alen = 0
483
484	for (_ssort_key in array)
485		_sort_alen++
486
487	if (_sort_alen <= 1)
488		return
489
490	# perform sort
491	_qsort(array, 0, _sort_alen-1)
492}
493
494function _qsort (array, first, last)
495{
496	if (first >= last)
497		return
498
499	# select pivot element
500	_qpivot = int(first + int((last-first+1) * rand()))
501	_qleft = first
502	_qright = last
503
504	_qpivot_val = array[_qpivot]
505
506	# partition
507	while (_qleft <= _qright) {
508		while (array[_qleft] < _qpivot_val)
509			_qleft++
510
511		while (array[_qright] > _qpivot_val)
512			_qright--
513
514		# swap
515		if (_qleft <= _qright) {
516			_qleft_val = array[_qleft]
517			_qright_val = array[_qright]
518
519			array[_qleft] = _qright_val
520			array[_qright] = _qleft_val
521
522			_qleft++
523			_qright--
524		}
525	}
526
527	# sort the partitions
528	_qsort(array, first, _qright)
529	_qsort(array, _qleft, last)
530}
531
532#
533# Print msg to output file, without indentation
534#
535function emit_ni (msg)
536{
537	printf("%s", msg) >> OUTPUT_FILE
538}
539
540#
541# Print msg to output file, indented for the current `output_depth`
542#
543function emit (msg)
544{
545	for (_ind = 0; _ind < output_depth; _ind++)
546		emit_ni("\t")
547
548	emit_ni(msg)
549}
550
551#
552# Print a warning to stderr
553#
554function warn (msg)
555{
556	print "warning:", msg, "at", FILENAME, "line", NR > "/dev/stderr"
557}
558
559#
560# Print a compiler error to stderr
561#
562function error (msg)
563{
564	errorx(msg " at " FILENAME " line " NR ":\n\t" $0)
565}
566
567#
568# Print an error message without including the source line information
569#
570function errorx (msg)
571{
572	print "error:", msg > "/dev/stderr"
573	_EARLY_EXIT=1
574	exit 1
575}
576
577#
578# Print a debug output message
579#
580function debug (msg)
581{
582	if (!DEBUG)
583		return
584	for (_di = 0; _di < depth; _di++)
585		printf("\t") > "/dev/stderr"
586	print msg > "/dev/stderr"
587}
588
589#
590# Return an array key composed of the given (parent, selector, child)
591# tuple.
592# The child argument is optional and may be omitted.
593#
594function subkey (parent, selector, child)
595{
596	if (child != null)
597		return (parent SUBSEP selector SUBSEP child)
598	else
599		return (parent SUBSEP selector)
600}
601
602#
603# Advance to the next non-comment input record
604#
605function next_line ()
606{
607	do {
608		_result = getline
609	} while (_result > 0 && $0 ~ /^[ \t]*#.*/) # skip comment lines
610	return (_result)
611}
612
613#
614# Advance to the next input record and verify that it matches @p regex
615#
616function getline_matching (regex)
617{
618	_result = next_line()
619	if (_result <= 0)
620		return (_result)
621
622	if ($0 ~ regex)
623		return (1)
624
625	return (-1)
626}
627
628#
629# Shift the current fields left by `n`.
630#
631# If all fields are consumed and the optional do_getline argument is true,
632# read the next line.
633#
634function shiftf (n, do_getline)
635{
636	if (n > NF) error("shift past end of line")
637	for (_si = 1; _si <= NF-n; _si++) {
638		$(_si) = $(_si+n)
639	}
640	NF = NF - n
641
642	if (NF == 0 && do_getline)
643		next_line()
644}
645
646#
647# Parse a revision descriptor from the current line.
648#
649function parse_revdesc (result)
650{
651	_rstart = 0
652	_rend = 0
653
654	if ($2 ~ "[0-9]*-[0-9*]") {
655		split($2, _revrange, "[ \t]*-[ \t]*")
656		_rstart = _revrange[1]
657		_rend = _revrange[2]
658	} else if ($2 ~ "(>|>=|<|<=)" && $3 ~ "[1-9][0-9]*") {
659		if ($2 == ">") {
660			_rstart = int($3)+1
661			_rend = REV_MAX
662		} else if ($2 == ">=") {
663			_rstart = int($3)
664			_rend = REV_MAX
665		} else if ($2 == "<" && int($3) > 0) {
666			_rstart = 0
667			_rend = int($3)-1
668		} else if ($2 == "<=") {
669			_rstart = 0
670			_rend = int($3)-1
671		} else {
672			error("invalid revision descriptor")
673		}
674	} else if ($2 ~ "[1-9][0-9]*") {
675		_rstart = int($2)
676		_rend = int($2)
677	} else {
678		error("invalid revision descriptor")
679	}
680
681	result[REV_START] = _rstart
682	result[REV_END] = _rend
683}
684
685#
686# Push a new parser state.
687#
688# The name may be null, in which case the STATE_IDENT variable will not be
689# defined in this scope
690#
691function push_state (type, name, block) {
692	depth++
693	push(STATE_LINENO, NR)
694	if (name != null)
695		push(STATE_IDENT, name)
696	push(STATE_TYPE, type)
697	push(STATE_ISBLOCK, block)
698}
699
700#
701# Pop the top of the parser state stack.
702#
703function pop_state () {
704	# drop all symbols defined at this depth
705	for (s in symbols) {
706		if (s ~ "^"depth"[^0-9]")
707			delete symbols[s]
708	}
709	depth--
710}
711
712#
713# Find opening brace and push a new parser state for a brace-delimited block.
714#
715# The name may be null, in which case the STATE_IDENT variable will not be
716# defined in this scope
717#
718function open_block (type, name)
719{
720	if ($0 ~ "{" || getline_matching("^[ \t]*{") > 0) {
721		push_state(type, name, 1)
722		sub("^[^{]+{", "", $0)
723		return
724	}
725
726	error("found '"$1 "' instead of expected '{' for '" name "'")
727}
728
729#
730# Find closing brace and pop parser states until the first
731# brace-delimited block is discarded.
732#
733function close_block ()
734{
735	if ($0 !~ "}")
736		error("internal error - no closing brace")
737
738	# pop states until we exit the first enclosing block
739	do {
740		_closed_block = g(STATE_ISBLOCK)
741		pop_state()
742	} while (!_closed_block)
743
744	# strip everything prior to the block closure
745	sub("^[^}]*}", "", $0)
746}
747
748# Internal symbol table lookup function. Returns the symbol depth if
749# name is found at or above scope; if scope is null, it defauls to 0
750function _find_sym (name, scope)
751{
752	if (scope == null)
753		scope = 0;
754
755	for (i = scope; i < depth; i++) {
756		if ((depth-i,name) in symbols)
757			return (depth-i)
758	}
759
760	return (-1)
761}
762
763#
764# Look up a variable in the symbol table with `name` and return its value.
765#
766# If `scope` is not null, the variable search will start at the provided
767# scope level -- 0 is the current scope, 1 is the parent's scope, etc.
768#
769function g (name, scope)
770{
771	_g_depth = _find_sym(name, scope)
772	if (_g_depth < 0)
773		error("'" name "' is undefined")
774
775	return (symbols[_g_depth,name])
776}
777
778function is_defined (name, scope)
779{
780	return (_find_sym(name, scope) >= 0)
781}
782
783# Define a new variable in the symbol table's current scope,
784# with the given value
785function push (name, value)
786{
787	symbols[depth,name] = value
788}
789
790# Set an existing variable's value in the symbol table; if not yet defined,
791# will trigger an error
792function set (name, value, scope)
793{
794	for (i = 0; i < depth; i++) {
795		if ((depth-i,name) in symbols) {
796			symbols[depth-i,name] = value
797			return
798		}
799	}
800	# No existing value, cannot define
801	error("'" name "' is undefined")
802}
803
804# Evaluates to true if immediately within a block scope of the given type
805function in_state (type)
806{
807	if (!is_defined(STATE_TYPE))
808		return (type == ST_NONE)
809
810	return (type == g(STATE_TYPE))
811}
812
813# Evaluates to true if within an immediate or non-immediate block scope of the
814# given type
815function in_nested_state (type)
816{
817	for (i = 0; i < depth; i++) {
818		if ((depth-i,STATE_TYPE) in symbols) {
819			if (symbols[depth-i,STATE_TYPE] == type)
820				return (1)
821		}
822	}
823	return (0)
824}
825
826# Evaluates to true if definitions of the given type are permitted within
827# the current scope
828function allow_def (type)
829{
830	if (type == ST_VAR_BLOCK) {
831		return (in_state(ST_NONE) || in_state(ST_STRUCT_BLOCK))
832	} else if (type == ST_STRUCT_BLOCK) {
833		return (in_state(ST_NONE))
834	} else if (type == ST_SROM_DEFN) {
835		return (in_state(ST_VAR_BLOCK) || in_state(ST_STRUCT_BLOCK))
836	}
837
838	error("unknown type '" type "'")
839}
840
841# struct definition
842$1 == ST_STRUCT_BLOCK && allow_def($1) {
843	name = $2
844
845	# Remove array[] specifier
846	if (sub(/\[\]$/, "", name) == 0)
847		error("expected '" name "[]', not '" name "'")
848
849	if (name !~ IDENT_REGEX || name ~ TYPES_REGEX)
850		error("invalid identifier '" name "'")
851
852	# Add top-level struct entry
853	if ((name,DEF_LINE) in structs)
854		error("struct identifier '" name "' previously defined on " \
855		    "line " structs[name,DEF_LINE])
856	structs[name,DEF_LINE] = NR
857	structs[name,NUM_REVS] = 0
858
859	# Open the block
860	debug("struct " name " {")
861	open_block(ST_STRUCT_BLOCK, name)
862}
863
864# struct srom descriptor
865$1 == ST_SROM_DEFN && allow_def(ST_SROM_DEFN) && in_state(ST_STRUCT_BLOCK) {
866	sid = g(STATE_IDENT)
867
868	# parse revision descriptor
869	rev_desc[REV_START] = 0
870	parse_revdesc(rev_desc)
871
872	# assign revision id
873	rev = structs[sid,NUM_REVS] ""
874	revk = subkey(sid, REV, rev)
875	structs[sid,NUM_REVS]++
876
877	# init basic revision state
878	structs[revk,REV_START] = rev_desc[REV_START]
879	structs[revk,REV_END] = rev_desc[REV_END]
880
881	if (match($0, "\\[[^]]*\\]") <= 0)
882		error("expected base address array")
883
884	addrs_str = substr($0, RSTART+1, RLENGTH-2)
885	num_offs = split(addrs_str, addrs, ",[ \t]*")
886	structs[revk, REV_NUM_OFFS] = num_offs
887	for (i = 1; i <= num_offs; i++) {
888		offk = subkey(revk, OFF, (i-1) "")
889
890		if (addrs[i] !~ HEX_REGEX)
891			error("invalid base address '" addrs[i] "'")
892
893		structs[offk,SEG_ADDR] = addrs[i]
894	}
895
896	debug("struct_srom " structs[revk,REV_START] "... [" addrs_str "]")
897	next
898}
899
900# close any previous srom revision descriptor
901$1 == ST_SROM_DEFN && in_state(ST_SROM_DEFN) {
902	pop_state()
903}
904
905# open a new srom revision descriptor
906$1 == ST_SROM_DEFN && allow_def(ST_SROM_DEFN) {
907	# parse revision descriptor
908	parse_revdesc(rev_desc)
909
910	# assign revision id
911	vid = g(STATE_IDENT)
912	rev = vars[vid,NUM_REVS] ""
913	revk = subkey(vid, REV, rev)
914	vars[vid,NUM_REVS]++
915
916	# vend scoped rev/revk variables for use in the
917	# revision offset block
918	push("rev_id", rev)
919	push("rev_key", revk)
920
921	# init basic revision state
922	vars[revk,DEF_LINE] = NR
923	vars[revk,REV_START] = rev_desc[REV_START]
924	vars[revk,REV_END] = rev_desc[REV_END]
925	vars[revk,REV_NUM_OFFS] = 0
926
927	debug("srom " rev_desc[REV_START] "-" rev_desc[REV_END] " {")
928	push_state(ST_SROM_DEFN, null, 0)
929
930	# seek to the first offset definition
931	do {
932		shiftf(1)
933	} while ($1 !~ SROM_OFF_REGEX && NF > 0)
934}
935
936#
937# Extract and return the array length from the given type string.
938# Returns -1 if the type is not an array.
939#
940function type_array_len (type)
941{
942	# extract byte count[] and width
943	if (match(type, ARRAY_REGEX"$") > 0) {
944		return (substr(type, RSTART+1, RLENGTH-2))
945	} else {
946		return (-1)
947	}
948}
949
950#
951# Parse an offset declaration from the current line.
952#
953function parse_offset_segment (revk, offk)
954{
955	vid = g(STATE_IDENT)
956
957	# use explicit type if specified, otherwise use the variable's
958	# common type
959	if ($1 !~ HEX_REGEX) {
960		type = $1
961		if (type !~ TYPES_REGEX)
962			error("unknown field type '" type "'")
963
964		shiftf(1)
965	} else {
966		type = vars[vid,VAR_TYPE]
967	}
968
969	# read offset value
970	offset = $1
971	if (offset !~ HEX_REGEX)
972		error("invalid offset value '" offset "'")
973
974	# extract byte count[], base type, and width
975	if (match(type, ARRAY_REGEX"$") > 0) {
976		count = int(substr(type, RSTART+1, RLENGTH-2))
977		type = substr(type, 1, RSTART-1)
978	} else {
979		count = 1
980	}
981	width = TSIZE[type]
982
983	# seek to attributes or end of the offset expr
984	sub("^[^,(|){}]+", "", $0)
985
986	# parse attributes
987	mask=TMASK[type]
988	shift=0
989
990	if ($1 ~ "^\\(") {
991		# extract attribute list
992		if (match($0, "\\([^|\(\)]*\\)") <= 0)
993			error("expected attribute list")
994		attr_str = substr($0, RSTART+1, RLENGTH-2)
995
996		# drop from input line
997		$0 = substr($0, RSTART+RLENGTH, length($0) - RSTART+RLENGTH)
998
999		# parse attributes
1000		num_attr = split(attr_str, attrs, ",[ \t]*")
1001		for (i = 1; i <= num_attr; i++) {
1002			attr = attrs[i]
1003			if (sub("^&[ \t]*", "", attr) > 0) {
1004				mask = attr
1005			} else if (sub("^<<[ \t]*", "", attr) > 0) {
1006				shift = "-"attr
1007			} else if (sub("^>>[ \t]*", "", attr) > 0) {
1008				shift = attr
1009			} else {
1010				error("unknown attribute '" attr "'")
1011			}
1012		}
1013	}
1014
1015	# assign segment id
1016	seg = vars[offk,OFF_NUM_SEGS] ""
1017	segk = subkey(offk, OFF_SEG, seg)
1018	vars[offk,OFF_NUM_SEGS]++
1019
1020	vars[segk,SEG_ADDR]	= offset + (width * _oi)
1021	vars[segk,SEG_COUNT]	= count
1022	vars[segk,SEG_TYPE]	= type
1023	vars[segk,SEG_MASK]	= mask
1024	vars[segk,SEG_SHIFT]	= shift
1025
1026	debug("{"vars[segk,SEG_ADDR]", "type", "mask", "shift"}" \
1027		_comma)
1028}
1029
1030# revision offset definition
1031$1 ~ SROM_OFF_REGEX && in_state(ST_SROM_DEFN) {
1032	vid = g(STATE_IDENT)
1033
1034	# fetch rev id/key defined by our parent block
1035	rev = g("rev_id")
1036	revk = g("rev_key")
1037
1038	# parse all offsets
1039	do {
1040		# assign offset id
1041		off = vars[revk,REV_NUM_OFFS] ""
1042		offk = subkey(revk, OFF, off)
1043		vars[revk,REV_NUM_OFFS]++
1044
1045		# initialize segment count
1046		vars[offk,DEF_LINE] = NR
1047		vars[offk,OFF_NUM_SEGS] = 0
1048
1049		debug("[")
1050		# parse all segments
1051		do {
1052			parse_offset_segment(revk, offk)
1053			_more_seg = ($1 == "|")
1054			if (_more_seg)
1055				shiftf(1, 1)
1056		} while (_more_seg)
1057		debug("],")
1058		_more_vals = ($1 == ",")
1059		if (_more_vals)
1060			shiftf(1, 1)
1061	} while (_more_vals)
1062}
1063
1064# variable definition
1065(($1 == "private" && $2 ~ TYPES_REGEX) || $1 ~ TYPES_REGEX) &&
1066    allow_def(ST_VAR_BLOCK) \
1067{
1068	# check for 'private' flag
1069	if ($1 == "private") {
1070		private = 1
1071		shiftf(1)
1072	} else {
1073		private = 0
1074	}
1075
1076	type = $1
1077	name = $2
1078	array = 0
1079	debug(type " " name " {")
1080
1081	# Check for and remove any array[] specifier
1082	base_type = type
1083	if (sub(ARRAY_REGEX"$", "", base_type) > 0)
1084		array = 1
1085
1086	# verify type
1087	if (!base_type in DTYPE)
1088		error("unknown type '" $1 "'")
1089
1090	# Add top-level variable entry
1091	if (name in var_names)
1092		error("variable identifier '" name "' previously defined on " \
1093		    "line " vars[name,DEF_LINE])
1094
1095	var_names[name] = 0
1096	vars[name,VAR_NAME] = name
1097	vars[name,DEF_LINE] = NR
1098	vars[name,VAR_TYPE] = type
1099	vars[name,VAR_BASE_TYPE] = base_type
1100	vars[name,NUM_REVS] = 0
1101	vars[name,VAR_PRIVATE] = private
1102	vars[name,VAR_ARRAY] = array
1103	vars[name,VAR_FMT] = "hex" # default if not specified
1104
1105	open_block(ST_VAR_BLOCK, name)
1106
1107	debug("type=" DTYPE[base_type])
1108
1109	if (in_nested_state(ST_STRUCT_BLOCK)) {
1110		# Fetch the enclosing struct's name
1111		sid = g(STATE_IDENT, 1)
1112
1113		# Mark as a struct-based variable
1114		vars[name,VAR_STRUCT] = sid
1115	}
1116}
1117
1118# variable parameters
1119$1 ~ IDENT_REGEX && $2 ~ IDENT_REGEX && in_state(ST_VAR_BLOCK) {
1120	vid = g(STATE_IDENT)
1121	if ($1 == PROP_T_SFMT) {
1122		if (!$2 in FMT)
1123			error("invalid fmt '" $2 "'")
1124
1125		vars[vid,VAR_FMT] = $2
1126		debug($1 "=" FMT[$2])
1127	} else if ($1 == PROP_T_ALL1 && $2 == "ignore") {
1128		vars[vid,VAR_IGNALL1] = 1
1129	} else {
1130		error("unknown parameter " $1)
1131	}
1132	next
1133}
1134
1135# Skip comments and blank lines
1136/^[ \t]*#/ || /^$/ {
1137	next
1138}
1139
1140# Close blocks
1141/}/ && !in_state(ST_NONE) {
1142	while (!in_state(ST_NONE) && $0 ~ "}") {
1143		close_block();
1144		debug("}")
1145	}
1146	next
1147}
1148
1149# Report unbalanced '}'
1150/}/ && in_state(ST_NONE) {
1151	error("extra '}'")
1152}
1153
1154# Invalid variable type
1155$1 && allow_def(ST_VAR_BLOCK) {
1156	error("unknown type '" $1 "'")
1157}
1158
1159# Generic parse failure
1160{
1161	error("unrecognized statement")
1162}
1163