4 # Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
7 # Redistribution and use in source and binary forms, with or without
8 # modification, are permitted provided that the following conditions
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.
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.
40 print "usage: bhnd_nvram_map.awk <input map> [-hd] [-o output file]"
50 # Probe awk implementation's hex digit handling
51 if ("0xA" + 0 != 10) {
57 OUT_T_HEADER = "HEADER"
60 # Tab width to use when calculating output alignment
73 for (_i = 1; _i < ARGC; _i++) {
74 if (ARGV[_i] == "--debug") {
76 } else if (ARGV[_i] == "-d" && OUT_T == null) {
78 } else if (ARGV[_i] == "-h" && OUT_T == null) {
80 } else if (ARGV[_i] == "-o") {
85 OUTPUT_FILE = ARGV[_i]
86 } else if (ARGV[_i] == "--") {
89 } else if (ARGV[_i] !~ /^-/) {
92 print "unknown option " ARGV[_i]
100 print("error: one of -d or -h required")
104 if (FILENAME == null) {
105 print("error: no input file specified")
109 if (OUTPUT_FILE == "-") {
110 OUTPUT_FILE = "/dev/stdout"
111 } else if (OUTPUT_FILE == null) {
112 OUTPUT_FILE_IDX = split(FILENAME, _g_output_path, "/")
113 OUTPUT_FILE = _g_output_path[OUTPUT_FILE_IDX]
115 if (OUTPUT_FILE !~ /^bhnd_/)
116 OUTPUT_FILE = "bhnd_" OUTPUT_FILE
118 if (OUT_T == OUT_T_HEADER)
119 OUTPUT_FILE = OUTPUT_FILE ".h"
121 OUTPUT_FILE = OUTPUT_FILE "_data.h"
125 UINT_REGEX = "^(0|[1-9][0-9]*)$"
126 HEX_REGEX = "^(0x[A-Fa-f0-9]+)$"
127 OFF_REGEX = "^(0|[1-9][0-9]*)|^(0x[A-Fa-f0-9]+)"
128 REL_OFF_REGEX = "^\\+(0|[1-9][0-9]*)|^\\+(0x[A-Fa-f0-9]+)"
130 ARRAY_REGEX = "\\[(0|[1-9][0-9]*)\\]"
131 TYPES_REGEX = "^(((u|i)(8|16|32))|char)("ARRAY_REGEX")?$"
133 IDENT_REGEX = "[A-Za-z_][A-Za-z0-9_]*"
134 SVAR_IDENT_REGEX = "^<"IDENT_REGEX">{?$" # <var> identifiers
135 VAR_IDENT_REGEX = "^"IDENT_REGEX"{?$" # var identifiers
137 VACCESS_REGEX = "^(private|internal)$"
139 # Property array keys
143 # Prop path array keys
144 PPATH_HEAD = "ppath_head"
145 PPATH_TAIL = "ppath_tail"
148 OBJ_IS_CLS = "o_is_cls"
149 OBJ_SUPER = "o_super"
153 CLS_NAME = "cls_name"
154 CLS_PROP = "cls_prop"
156 # C SPROM binding opcodes/opcode flags
157 SPROM_OPCODE_EOF = "SPROM_OPCODE_EOF"
158 SPROM_OPCODE_NELEM = "SPROM_OPCODE_NELEM"
159 SPROM_OPCODE_VAR_END = "SPROM_OPCODE_VAR_END"
160 SPROM_OPCODE_VAR_IMM = "SPROM_OPCODE_VAR_IMM"
161 SPROM_OPCODE_VAR_REL_IMM = "SPROM_OPCODE_VAR_REL_IMM"
162 SPROM_OPCODE_VAR = "SPROM_OPCODE_VAR"
163 SPROM_OPCODE_REV_IMM = "SPROM_OPCODE_REV_IMM"
164 SPROM_OPCODE_REV_RANGE = "SPROM_OPCODE_REV_RANGE"
165 SPROM_OP_REV_START_MASK = "SPROM_OP_REV_START_MASK"
166 SPROM_OP_REV_START_SHIFT = "SPROM_OP_REV_START_SHIFT"
167 SPROM_OP_REV_END_MASK = "SPROM_OP_REV_END_MASK"
168 SPROM_OP_REV_END_SHIFT = "SPROM_OP_REV_END_SHIFT"
169 SPROM_OPCODE_MASK_IMM = "SPROM_OPCODE_MASK_IMM"
170 SPROM_OPCODE_MASK = "SPROM_OPCODE_MASK"
171 SPROM_OPCODE_SHIFT_IMM = "SPROM_OPCODE_SHIFT_IMM"
172 SPROM_OPCODE_SHIFT = "SPROM_OPCODE_SHIFT"
173 SPROM_OPCODE_OFFSET_REL_IMM = "SPROM_OPCODE_OFFSET_REL_IMM"
174 SPROM_OPCODE_OFFSET = "SPROM_OPCODE_OFFSET"
175 SPROM_OPCODE_TYPE = "SPROM_OPCODE_TYPE"
176 SPROM_OPCODE_TYPE_IMM = "SPROM_OPCODE_TYPE_IMM"
177 SPROM_OPCODE_DO_BINDN_IMM = "SPROM_OPCODE_DO_BINDN_IMM"
178 SPROM_OPCODE_DO_BIND = "SPROM_OPCODE_DO_BIND"
179 SPROM_OPCODE_DO_BINDN = "SPROM_OPCODE_DO_BINDN"
180 SPROM_OP_BIND_SKIP_IN_MASK = "SPROM_OP_BIND_SKIP_IN_MASK"
181 SPROM_OP_BIND_SKIP_IN_SHIFT = "SPROM_OP_BIND_SKIP_IN_SHIFT"
182 SPROM_OP_BIND_SKIP_IN_SIGN = "SPROM_OP_BIND_SKIP_IN_SIGN"
183 SPROM_OP_BIND_SKIP_OUT_MASK = "SPROM_OP_BIND_SKIP_OUT_MASK"
184 SPROM_OP_BIND_SKIP_OUT_SHIFT = "SPROM_OP_BIND_SKIP_OUT_SHIFT"
186 SPROM_OP_DATA_U8 = "SPROM_OP_DATA_U8"
187 SPROM_OP_DATA_U8_SCALED = "SPROM_OP_DATA_U8_SCALED"
188 SPROM_OP_DATA_U16 = "SPROM_OP_DATA_U16"
189 SPROM_OP_DATA_U32 = "SPROM_OP_DATA_U32"
190 SPROM_OP_DATA_I8 = "SPROM_OP_DATA_I8"
192 SPROM_OP_BIND_SKIP_IN_MAX = 3 # maximum SKIP_IN value
193 SPROM_OP_BIND_SKIP_IN_MIN = -3 # minimum SKIP_IN value
194 SPROM_OP_BIND_SKIP_OUT_MAX = 1 # maximum SKIP_OUT value
195 SPROM_OP_BIND_SKIP_OUT_MIN = 0 # minimum SKIP_OUT value
196 SPROM_OP_IMM_MAX = 15 # maximum immediate value
197 SPROM_OP_REV_RANGE_MAX = 15 # maximum SROM rev range value
199 # SPROM opcode encoding state
200 SromOpStream = class_new("SromOpStream")
201 class_add_prop(SromOpStream, p_layout, "layout")
202 class_add_prop(SromOpStream, p_revisions, "revisions")
203 class_add_prop(SromOpStream, p_vid, "vid")
204 class_add_prop(SromOpStream, p_offset, "offset")
205 class_add_prop(SromOpStream, p_type, "type")
206 class_add_prop(SromOpStream, p_nelem, "nelem")
207 class_add_prop(SromOpStream, p_mask, "mask")
208 class_add_prop(SromOpStream, p_shift, "shift")
209 class_add_prop(SromOpStream, p_bind_total, "bind_total")
210 class_add_prop(SromOpStream, p_pending_bind, "pending_bind")
212 # SROM pending bind operation
213 SromOpBind = class_new("SromOpBind")
214 class_add_prop(SromOpBind, p_segment, "segment")
215 class_add_prop(SromOpBind, p_count, "count")
216 class_add_prop(SromOpBind, p_offset, "offset")
217 class_add_prop(SromOpBind, p_width, "width")
218 class_add_prop(SromOpBind, p_skip_in, "skip_in")
219 class_add_prop(SromOpBind, p_skip_out, "skip_out")
220 class_add_prop(SromOpBind, p_buffer, "buffer")
222 # Map class definition
223 Map = class_new("Map")
225 # Array class definition
226 Array = class_new("Array")
227 class_add_prop(Array, p_count, "count")
229 # MacroType class definition
230 # Used to define a set of known macro types that may be generated
231 MacroType = class_new("MacroType")
232 class_add_prop(MacroType, p_name, "name")
233 class_add_prop(MacroType, p_const_suffix, "const_suffix")
235 MTypeVarName = macro_type_new("name", "") # var name
236 MTypeVarID = macro_type_new("id", "_ID") # var unique ID
237 MTypeVarMaxLen = macro_type_new("len", "_MAXLEN") # var max array length
239 # Preprocessor Constant
240 MacroDefine = class_new("MacroDefine")
241 class_add_prop(MacroDefine, p_name, "name")
242 class_add_prop(MacroDefine, p_value, "value")
244 # ParseState definition
245 ParseState = class_new("ParseState")
246 class_add_prop(ParseState, p_ctx, "ctx")
247 class_add_prop(ParseState, p_is_block, "is_block")
248 class_add_prop(ParseState, p_line, "line")
251 Fmt = class_new("Fmt")
252 class_add_prop(Fmt, p_name, "name")
253 class_add_prop(Fmt, p_symbol, "symbol")
254 class_add_prop(Fmt, p_array_fmt, "array_fmt")
256 FmtHex = fmt_new("hex", "bhnd_nvram_val_bcm_hex_fmt")
257 FmtDec = fmt_new("decimal", "bhnd_nvram_val_bcm_decimal_fmt")
258 FmtMAC = fmt_new("macaddr", "bhnd_nvram_val_bcm_macaddr_fmt")
259 FmtLEDDC = fmt_new("leddc", "bhnd_nvram_val_bcm_leddc_fmt")
260 FmtCharArray = fmt_new("char_array", "bhnd_nvram_val_char_array_fmt")
261 FmtChar = fmt_new("char", "bhnd_nvram_val_char_array_fmt",
263 FmtStr = fmt_new("string", "bhnd_nvram_val_bcm_string_fmt")
265 # User-specifiable value formats
266 ValueFormats = map_new()
267 map_set(ValueFormats, get(FmtHex, p_name), FmtHex)
268 map_set(ValueFormats, get(FmtDec, p_name), FmtDec)
269 map_set(ValueFormats, get(FmtMAC, p_name), FmtMAC)
270 map_set(ValueFormats, get(FmtLEDDC, p_name), FmtLEDDC)
271 map_set(ValueFormats, get(FmtStr, p_name), FmtStr)
274 Type = class_new("Type")
275 class_add_prop(Type, p_name, "name")
276 class_add_prop(Type, p_width, "width")
277 class_add_prop(Type, p_signed, "signed")
278 class_add_prop(Type, p_const, "const")
279 class_add_prop(Type, p_const_val, "const_val")
280 class_add_prop(Type, p_array_const, "array_const")
281 class_add_prop(Type, p_array_const_val, "array_const_val")
282 class_add_prop(Type, p_default_fmt, "default_fmt")
283 class_add_prop(Type, p_mask, "mask")
285 ArrayType = class_new("ArrayType", AST)
286 class_add_prop(ArrayType, p_type, "type")
287 class_add_prop(ArrayType, p_count, "count")
291 UInt32Max = 4294967295
296 Int32Min = -2147483648
297 Int32Max = 2147483648
301 UInt8 = type_new("u8", 1, 0, "BHND_NVRAM_TYPE_UINT8",
302 "BHND_NVRAM_TYPE_UINT8_ARRAY", FmtHex, UInt8Max, 0, 16)
304 UInt16 = type_new("u16", 2, 0, "BHND_NVRAM_TYPE_UINT16",
305 "BHND_NVRAM_TYPE_UINT16_ARRAY", FmtHex, UInt16Max, 1, 17)
307 UInt32 = type_new("u32", 4, 0, "BHND_NVRAM_TYPE_UINT32",
308 "BHND_NVRAM_TYPE_UINT32_ARRAY", FmtHex, UInt32Max, 2, 18)
310 Int8 = type_new("i8", 1, 1, "BHND_NVRAM_TYPE_INT8",
311 "BHND_NVRAM_TYPE_INT8_ARRAY", FmtDec, UInt8Max, 4, 20)
313 Int16 = type_new("i16", 2, 1, "BHND_NVRAM_TYPE_INT16",
314 "BHND_NVRAM_TYPE_INT16_ARRAY", FmtDec, UInt16Max, 5, 21)
316 Int32 = type_new("i32", 4, 1, "BHND_NVRAM_TYPE_INT32",
317 "BHND_NVRAM_TYPE_INT32_ARRAY", FmtDec, UInt32Max, 6, 22)
319 Char = type_new("char", 1, 1, "BHND_NVRAM_TYPE_CHAR",
320 "BHND_NVRAM_TYPE_CHAR_ARRAY", FmtChar, UInt8Max, 8, 24)
322 BaseTypes = map_new()
323 map_set(BaseTypes, get(UInt8, p_name), UInt8)
324 map_set(BaseTypes, get(UInt16, p_name), UInt16)
325 map_set(BaseTypes, get(UInt32, p_name), UInt32)
326 map_set(BaseTypes, get(Int8, p_name), Int8)
327 map_set(BaseTypes, get(Int16, p_name), Int16)
328 map_set(BaseTypes, get(Int32, p_name), Int32)
329 map_set(BaseTypes, get(Char, p_name), Char)
331 BaseTypesArray = map_to_array(BaseTypes)
332 BaseTypesCount = array_size(BaseTypesArray)
335 VFlag = class_new("VFlag")
336 class_add_prop(VFlag, p_name, "name")
337 class_add_prop(VFlag, p_const, "const")
339 VFlagPrivate = vflag_new("private", "BHND_NVRAM_VF_MFGINT")
340 VFlagIgnoreAll1 = vflag_new("ignall1", "BHND_NVRAM_VF_IGNALL1")
342 # Variable Access Type Constants
343 VAccess = class_new("VAccess")
344 VAccessPublic = obj_new(VAccess) # Public
345 VAccessPrivate = obj_new(VAccess) # MFG Private
346 VAccessInternal = obj_new(VAccess) # Implementation-Internal
351 AST = class_new("AST")
352 class_add_prop(AST, p_line, "line")
354 SymbolContext = class_new("SymbolContext", AST)
355 class_add_prop(SymbolContext, p_vars, "vars")
357 # NVRAM root parser context
358 NVRAM = class_new("NVRAM", SymbolContext)
359 class_add_prop(NVRAM, p_var_groups, "var_groups")
360 class_add_prop(NVRAM, p_srom_layouts, "srom_layouts")
361 class_add_prop(NVRAM, p_srom_table, "srom_table")
364 VarGroup = class_new("VarGroup", SymbolContext)
365 class_add_prop(VarGroup, p_name, "name")
368 RevRange = class_new("RevRange", AST)
369 class_add_prop(RevRange, p_start, "start")
370 class_add_prop(RevRange, p_end, "end")
373 StringConstant = class_new("StringConstant", AST)
374 class_add_prop(StringConstant, p_value, "value") # string
375 class_add_prop(StringConstant, p_continued, "continued") # bool
377 # Variable Declaration
378 Var = class_new("Var", AST)
379 class_add_prop(Var, p_access, "access") # VAccess
380 class_add_prop(Var, p_name, "name") # string
381 class_add_prop(Var, p_desc, "desc") # StringConstant
382 class_add_prop(Var, p_help, "help") # StringConstant
383 class_add_prop(Var, p_type, "type") # AbstractType
384 class_add_prop(Var, p_fmt, "fmt") # Fmt
385 class_add_prop(Var, p_ignall1, "ignall1") # bool
386 # ID is assigned once all variables are sorted
387 class_add_prop(Var, p_vid, "vid") # int
389 # Common interface inherited by parser contexts that support
390 # registration of SROM variable entries
391 SromContext = class_new("SromContext", AST)
392 class_add_prop(SromContext, p_revisions, "revisions")
395 SromLayout = class_new("SromLayout", SromContext)
396 class_add_prop(SromLayout, p_entries, "entries") # Array<SromEntry>
397 class_add_prop(SromLayout, p_revmap, "revmap") # Map<(string,int), SromEntry>
398 class_add_prop(SromLayout, p_output_var_counts, # Map<int, int> (rev->count)
401 # SROM Layout Filter Node
402 # Represents a filter over a parent SromLayout's revisions
403 SromLayoutFilter = class_new("SromLayoutFilter", SromContext)
404 class_add_prop(SromLayoutFilter, p_parent, "parent")
406 # SROM variable entry
407 SromEntry = class_new("SromEntry", AST)
408 class_add_prop(SromEntry, p_var, "var")
409 class_add_prop(SromEntry, p_revisions, "revisions")
410 class_add_prop(SromEntry, p_base_offset, "base_offset")
411 class_add_prop(SromEntry, p_type, "type")
412 class_add_prop(SromEntry, p_offsets, "offsets")
414 # SROM variable offset
415 SromOffset = class_new("SromOffset", AST)
416 class_add_prop(SromOffset, p_segments, "segments")
418 # SROM variable offset segment
419 SromSegment = class_new("SromSegment", AST)
420 class_add_prop(SromSegment, p_offset, "offset")
421 class_add_prop(SromSegment, p_type, "type")
422 class_add_prop(SromSegment, p_mask, "mask")
423 class_add_prop(SromSegment, p_shift, "shift")
424 class_add_prop(SromSegment, p_value, "value")
426 # Create the parse state stack
427 _g_parse_stack_depth = 0
428 _g_parse_stack[0] = null
430 # Push the root parse state
431 parser_state_push(nvram_new(), 0)
434 function at_exit(_block_start, _state, _output_vars, _noutput_vars, _name, _var,
437 # Skip completion handling if exiting from an error
441 # Check for complete block closure
442 if (!in_parser_context(NVRAM)) {
443 _state = parser_state_get()
444 _block_start = get(_state, p_line)
445 errorx("missing '}' for block opened on line " _block_start "")
448 # Apply lexicographical sorting to our variable names. To support more
449 # effecient table searching, we guarantee a stable sort order (using C
452 # This also has a side-effect of generating a unique monotonic ID
453 # for all variables, which we will emit as a #define and can use as a
454 # direct index into the C variable table
455 _output_vars = array_new()
456 for (_name in _g_var_names) {
457 _var = _g_var_names[_name]
459 # Don't include internal variables in the output
460 if (var_is_internal(_var))
463 array_append(_output_vars, _var)
466 # Sort by variable name
467 array_sort(_output_vars, prop_to_path(p_name))
469 # Set all variable ID properties to their newly assigned ID value
470 _noutput_vars = array_size(_output_vars)
471 for (_i = 0; _i < _noutput_vars; _i++) {
472 _var = array_get(_output_vars, _i)
476 # Truncate output file and write common header
477 printf("") > OUTPUT_FILE
479 emit(" * THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT.\n")
481 emit(" * generated from nvram map: " FILENAME "\n")
485 # Emit all variable definitions
486 if (OUT_T == OUT_T_DATA) {
487 write_data(_output_vars)
488 } else if (OUT_T == OUT_T_HEADER) {
489 write_header(_output_vars)
492 printf("%u variable records written to %s\n", array_size(_output_vars),
493 OUTPUT_FILE) >> "/dev/stderr"
496 # Write the public header (output type HEADER)
497 function write_header(output_vars, _noutput_vars, _var,
498 _tab_align, _macro, _macros, _num_macros, _i)
500 # Produce our array of #defines
502 _noutput_vars = array_size(output_vars)
503 for (_i = 0; _i < _noutput_vars; _i++) {
504 _var = array_get(output_vars, _i)
507 _macro = var_get_macro(_var, MTypeVarName, \
508 "\"" get(_var, p_name) "\"")
509 _macros[_num_macros++] = _macro
511 # Variable array length
512 if (var_has_array_type(_var)) {
513 _macro = var_get_macro(_var, MTypeVarMaxLen,
514 var_get_array_len(_var))
515 _macros[_num_macros++] = _macro
519 # Calculate value tab alignment position for our macros
520 _tab_align = macros_get_tab_alignment(_macros, _num_macros)
523 for (_i = 0; _i < _num_macros; _i++)
524 write_macro_define(_macros[_i], _tab_align)
527 # Write the private data header (output type DATA)
528 function write_data(output_vars, _noutput_vars, _var, _nvram, _layouts,
529 _nlayouts, _layout, _revs, _rev, _rev_start, _rev_end, _base_type,
530 _srom_table, _nsrom_table, _i, _j)
532 _nvram = parser_state_get_context(NVRAM)
533 _layouts = get(_nvram, p_srom_layouts)
534 _nlayouts = array_size(_layouts)
536 _noutput_vars = array_size(output_vars)
538 # Write all our private NVAR_ID defines
539 write_data_defines(output_vars)
541 # Write all layout binding opcodes, and build an array
542 # mapping SROM revision to corresponding SROM layout
543 _srom_table = array_new()
544 for (_i = 0; _i < _nlayouts; _i++) {
545 _layout = array_get(_layouts, _i)
547 # Write binding opcode table to our output file
548 write_srom_bindings(_layout)
550 # Add entries to _srom_table for all covered revisions
551 _revs = get(_layout, p_revisions)
552 _rev_start = get(_revs, p_start)
553 _rev_end = get(_revs, p_end)
555 for (_j = _rev_start; _j <= _rev_end; _j++)
556 array_append(_srom_table, _j)
559 # Sort in ascending order, by SROM revision
560 array_sort(_srom_table)
561 _nsrom_table = array_size(_srom_table)
563 # Write the variable definitions
564 emit("/* Variable definitions */\n")
565 emit("const struct bhnd_nvram_vardefn " \
566 "bhnd_nvram_vardefns[] = {\n")
568 for (_i = 0; _i < _noutput_vars; _i++) {
569 write_data_nvram_vardefn(array_get(output_vars, _i))
573 emit("const size_t bhnd_nvram_num_vardefns = " _noutput_vars ";\n")
575 # Write static asserts for raw type constant values that must be kept
576 # synchronized with the code
577 for (_i = 0; _i < BaseTypesCount; _i++) {
578 _base_type = array_get(BaseTypesArray, _i)
580 emit(sprintf("_Static_assert(%s == %u, \"%s\");\n",
581 type_get_const(_base_type), type_get_const_val(_base_type),
582 "type constant out of sync"))
584 emit(sprintf("_Static_assert(%s == %u, \"%s\");\n",
585 get(_base_type, p_array_const),
586 get(_base_type, p_array_const_val),
587 "array type constant out of sync"))
590 # Write all top-level bhnd_sprom_layout entries
591 emit("/* SPROM layouts */\n")
592 emit("const struct bhnd_sprom_layout bhnd_sprom_layouts[] = {\n")
594 for (_i = 0; _i < _nsrom_table; _i++) {
595 _rev = array_get(_srom_table, _i)
596 _layout = nvram_get_srom_layout(_nvram, _rev)
597 write_data_srom_layout(_layout, _rev)
601 emit("const size_t bhnd_sprom_num_layouts = " _nsrom_table ";\n")
604 # Write a bhnd_nvram_vardef entry for the given variable
605 function write_data_nvram_vardefn(v, _desc, _help, _type, _fmt) {
606 obj_assert_class(v, Var)
608 _desc = get(v, p_desc)
609 _help = get(v, p_help)
610 _type = get(v, p_type)
611 _fmt = var_get_fmt(v)
615 emit(sprintf(".name = \"%s\",\n", get(v, p_name)))
618 emit(sprintf(".desc = \"%s\",\n", get(_desc, p_value)))
620 emit(".desc = NULL,\n")
623 emit(sprintf(".help = \"%s\",\n", get(_help, p_value)))
625 emit(".help = NULL,\n")
627 emit(".type = " type_get_const(_type) ",\n")
628 emit(".nelem = " var_get_array_len(v) ",\n")
629 emit(".fmt = &" get(_fmt, p_symbol) ",\n")
630 emit(".flags = " gen_var_flags(v) ",\n")
636 # Write a top-level bhnd_sprom_layout entry for the given revision
637 # and layout definition
638 function write_data_srom_layout(layout, revision, _flags, _size,
639 _sromcrc, _crc_seg, _crc_off,
640 _sromsig, _sig_seg, _sig_offset, _sig_value,
641 _sromrev, _rev_seg, _rev_off,
646 # Calculate the size; it always follows the internal CRC variable
647 _sromcrc = srom_layout_find_entry(layout, "<sromcrc>", revision)
648 if (_sromcrc == null) {
649 errorx("missing '<sromcrc>' entry for '"revision"' layout, " \
650 "cannot compute total size")
652 _crc_seg = srom_entry_get_single_segment(_sromcrc)
653 _crc_off = get(_crc_seg, p_offset)
655 _size += get(get(_crc_seg, p_type), p_width)
658 # Fetch signature definition
659 _sromsig = srom_layout_find_entry(layout, "<sromsig>", revision)
660 if (_sromsig == null) {
661 array_append(_flags, "SPROM_LAYOUT_MAGIC_NONE")
663 _sig_seg = srom_entry_get_single_segment(_sromsig)
665 _sig_offset = get(_sig_seg, p_offset)
666 _sig_value = get(_sig_seg, p_value)
667 if (_sig_value == "")
668 errorc(get(_sromsig, p_line), "missing signature value")
671 # Fetch sromrev definition
672 _sromrev = srom_layout_find_entry(layout, "sromrev", revision)
673 if (_sromrev == null) {
674 errorx("missing 'sromrev' entry for '"revision"' layout, " \
675 "cannot determine offset")
678 if (!type_equal(get(_sromrev, p_type), UInt8)) {
679 errorx("'sromrev' entry has non-u8 type '" \
680 type_to_string(get(_sromrev, p_type)))
683 _rev_seg = srom_entry_get_single_segment(_sromrev)
684 _rev_off = get(_rev_seg, p_offset)
690 emit(".size = "_size",\n")
691 emit(".rev = "revision",\n")
693 if (array_size(_flags) > 0) {
694 emit(".flags = " array_join(_flags, "|") ",\n")
696 emit(".flags = 0,\n")
699 emit(".srev_offset = " _rev_off ",\n")
701 if (_sromsig != null) {
702 emit(".magic_offset = " _sig_offset ",\n")
703 emit(".magic_value = " _sig_value ",\n")
705 emit(".magic_offset = 0,\n")
706 emit(".magic_value = 0,\n")
709 emit(".crc_offset = " _crc_off ",\n")
711 emit(".bindings = " srom_layout_get_variable_name(layout) ",\n")
712 emit(".bindings_size = nitems(" \
713 srom_layout_get_variable_name(layout) "),\n")
715 emit(".num_vars = " srom_layout_num_output_vars(layout, revision) ",\n")
723 # Create a new opstream encoding state instance for the given layout
724 function srom_ops_new(layout, _obj) {
725 obj_assert_class(layout, SromLayout)
727 _obj = obj_new(SromOpStream)
728 set(_obj, p_layout, layout)
729 set(_obj, p_revisions, get(layout, p_revisions))
731 set(_obj, p_offset, 0)
732 set(_obj, p_type, null)
733 set(_obj, p_mask, null)
734 set(_obj, p_shift, null)
739 # Return the current type width, or throw an error if no type is currently
741 function srom_ops_get_type_width(opstream, _type)
743 obj_assert_class(opstream, SromOpStream)
745 _type = get(opstream, p_type)
747 errorx("no type value set")
749 return (get(type_get_base(_type), p_width))
752 # Write a string to the SROM opcode stream, either buffering the write,
753 # or emitting it directly.
754 function srom_ops_emit(opstream, string, _pending_bind, _buffer) {
755 obj_assert_class(opstream, SromOpStream)
758 if ((_pending_bind = get(opstream, p_pending_bind)) != null) {
759 _buffer = get(_pending_bind, p_buffer)
760 array_append(_buffer, string)
768 # Emit a SROM opcode followed by up to four optional bytes
769 function srom_ops_emit_opcode(opstream, opcode, arg0, arg1, arg2, arg3) {
770 obj_assert_class(opstream, SromOpStream)
772 srom_ops_emit(opstream, opcode",\n")
773 if (arg0 != "") srom_ops_emit(opstream, arg0",\n")
774 if (arg1 != "") srom_ops_emit(opstream, arg1",\n")
775 if (arg2 != "") srom_ops_emit(opstream, arg2",\n")
776 if (arg3 != "") srom_ops_emit(opstream, arg3",\n")
779 # Emit a SROM opcode and associated integer value, choosing the best
780 # SROM_OP_DATA variant for encoding the value.
782 # opc: The standard opcode for non-IMM encoded data, or null if none
783 # opc_imm: The IMM opcode, or null if none
784 # value: The value to encode
785 # svalue: Symbolic representation of value to include in output, or null
786 function srom_ops_emit_int_opcode(opstream, opc, opc_imm, value, svalue,
787 _width, _offset, _delta)
789 obj_assert_class(opstream, SromOpStream)
791 # Fetch current type width
792 _width = srom_ops_get_type_width(opstream)
795 if (opc_imm == SPROM_OPCODE_SHIFT_IMM) {
796 # SHIFT_IMM -- the imm value must be positive and divisible by
797 # two (shift/2) to use the IMM form.
798 if (value >= 0 && value % 2 == 0) {
804 } else if (opc_imm == SPROM_OPCODE_OFFSET_REL_IMM) {
805 # OFFSET_REL_IMM -- the imm value must be positive, divisible
806 # by the type width, and relative to the last offset to use
809 # Assert that callers correctly flushed any pending bind before
810 # attempting to set a relative offset
811 if (get(opstream, p_pending_bind) != null)
812 errorx("can't set relative offset with a pending bind")
814 # Fetch current offset, calculate relative value and determine
815 # whether we can issue an IMM opcode
816 _offset = get(opstream, p_offset)
817 _delta = value - _offset
819 _delta % _width == 0 &&
820 (_delta/_width) <= SPROM_OP_IMM_MAX)
822 srom_ops_emit(opstream,
823 sprintf("/* %#x + %#x -> %#x */\n", _offset,
825 value = (_delta / _width)
832 # If no symbolic representation provided, write the raw value
836 # Try to encode as IMM value?
837 if (opc_imm != null && value >= 0 && value <= SPROM_OP_IMM_MAX) {
838 srom_ops_emit_opcode(opstream, "("opc_imm"|"svalue")")
842 # Can't encode as immediate; do we have a non-immediate form?
844 errorx("can't encode '" value "' as immediate, and no " \
845 "non-immediate form was provided")
847 # Determine and emit minimal encoding
848 # We let the C compiler perform the bit operations, rather than
849 # trying to wrestle awk's floating point arithmetic
853 errorx("cannot int8 encode '" value "'")
855 srom_ops_emit_opcode(opstream,
856 "("opc"|"SPROM_OP_DATA_I8")", svalue)
858 } else if (value <= UInt8Max) {
860 srom_ops_emit_opcode(opstream,
861 "("opc"|"SPROM_OP_DATA_U8")", svalue)
863 } else if (value % _width == 0 && (value / _width) <= UInt8Max) {
865 srom_ops_emit_opcode(opstream,
866 "("opc"|"SPROM_OP_DATA_U8_SCALED")", svalue / _width)
868 } else if (value <= UInt16Max) {
870 srom_ops_emit_opcode(opstream,
871 "("opc"|"SPROM_OP_DATA_U16")",
875 } else if (value <= UInt32Max) {
877 srom_ops_emit_opcode(opstream,
878 "("opc"|"SPROM_OP_DATA_U32")",
880 "(("svalue" >> 8) & 0xFF)",
881 "(("svalue" >> 16) & 0xFF)",
882 "(("svalue" >> 24) & 0xFF)")
885 errorx("can't encode '" value "' (too large)")
889 # Emit initial OPCODE_VAR opcode and update opstream state
890 function srom_ops_reset_var(opstream, var, _vid_prev, _vid, _vid_name,
893 obj_assert_class(opstream, SromOpStream)
894 obj_assert_class(var, Var)
896 # Flush any pending bind for the previous variable
897 srom_ops_flush_bind(opstream, 1)
899 # Fetch current state
900 _vid_prev = get(opstream, p_vid)
902 _vid = get(var, p_vid)
903 _vid_name = var_get_macro_name(var, MTypeVarID)
906 _type = get(var, p_type)
907 set(opstream, p_vid, _vid)
908 set(opstream, p_type, type_get_base(_type))
909 set(opstream, p_nelem, var_get_array_len(var))
910 set(opstream, p_mask, type_get_default_mask(_type))
911 set(opstream, p_shift, 0)
912 set(opstream, p_bind_total, 0)
914 # Always provide a human readable comment
915 srom_ops_emit(opstream, sprintf("/* %s (%#x) */\n", get(var, p_name),
916 get(opstream, p_offset)))
918 # Prefer a single VAR_IMM byte
919 if (_vid_prev == 0 || _vid <= SPROM_OP_IMM_MAX) {
920 srom_ops_emit_int_opcode(opstream,
921 null, SPROM_OPCODE_VAR_IMM,
926 # Try encoding as a single VAR_REL_IMM byte
927 if (_vid_prev <= _vid && (_vid - _vid_prev) <= SPROM_OP_IMM_MAX) {
928 srom_ops_emit_int_opcode(opstream,
929 null, SPROM_OPCODE_VAR_REL_IMM,
930 _vid - _vid_prev, null)
934 # Fall back on a multibyte encoding
935 srom_ops_emit_int_opcode(opstream, SPROM_OPCODE_VAR, null, _vid,
939 # Emit OPCODE_REV/OPCODE_REV_RANGE (if necessary) for a new revision range
940 function srom_ops_emit_revisions(opstream, revisions, _prev_revs,
943 obj_assert_class(opstream, SromOpStream)
944 _prev_revs = get(opstream, p_revisions)
946 if (revrange_equal(_prev_revs, revisions))
949 # Update stream state
950 set(opstream, p_revisions, revisions)
952 _start = get(revisions, p_start)
953 _end = get(revisions, p_end)
955 # Sanity-check range values
956 if (_start < 0 || _end < 0)
957 errorx("invalid range: " revrange_to_string(revisions))
959 # If range covers a single revision, and can be encoded within
960 # SROM_OP_IMM_MAX, we can use the single byte encoding
961 if (_start == _end && _start <= SPROM_OP_IMM_MAX) {
962 srom_ops_emit_int_opcode(opstream,
963 null, SPROM_OPCODE_REV_IMM, _start)
967 # Otherwise, we need to use the two byte range encoding
968 if (_start > SPROM_OP_REV_RANGE_MAX || _end > SPROM_OP_REV_RANGE_MAX) {
969 errorx(sprintf("cannot encode range values %s (>= %u)",
970 revrange_to_string(revisions), SPROM_OP_REV_RANGE_MAX))
973 srom_ops_emit_opcode(opstream,
974 SPROM_OPCODE_REV_RANGE,
975 sprintf("(%u << %s) | (%u << %s)",
976 _start, SPROM_OP_REV_START_SHIFT,
977 _end, SPROM_OP_REV_END_SHIFT))
980 # Emit OPCODE_OFFSET (if necessary) for a new offset
981 function srom_ops_emit_offset(opstream, offset, _prev_offset, _rel_offset,
984 obj_assert_class(opstream, SromOpStream)
986 # Flush any pending bind before adjusting the offset
987 srom_ops_flush_bind(opstream, 0)
989 # Fetch current offset
990 _prev_offset = get(opstream, p_offset)
991 if (_prev_offset == offset)
994 # Encode (possibly a relative, 1-byte form) of the offset opcode
995 srom_ops_emit_int_opcode(opstream, SPROM_OPCODE_OFFSET,
996 SPROM_OPCODE_OFFSET_REL_IMM, offset, null)
999 set(opstream, p_offset, offset)
1002 # Emit OPCODE_TYPE (if necessary) for a new type value; this also
1003 # resets the mask to the type default.
1004 function srom_ops_emit_type(opstream, type, _base_type, _prev_type, _prev_mask,
1007 obj_assert_class(opstream, SromOpStream)
1008 if (!obj_is_instanceof(type, ArrayType))
1009 obj_assert_class(type, Type)
1011 _default_mask = type_get_default_mask(type)
1012 _base_type = type_get_base(type)
1014 # If state already matches the requested type, nothing to be
1016 _prev_type = get(opstream, p_type)
1017 _prev_mask = get(opstream, p_mask)
1018 if (type_equal(_prev_type, _base_type) && _prev_mask == _default_mask)
1022 set(opstream, p_type, _base_type)
1023 set(opstream, p_mask, _default_mask)
1026 if (type_get_const_val(_base_type) <= SPROM_OP_IMM_MAX) {
1027 # Single byte IMM encoding
1028 srom_ops_emit_opcode(opstream,
1029 SPROM_OPCODE_TYPE_IMM "|" type_get_const(_base_type))
1032 srom_ops_emit_opcode(opstream, SPROM_OPCODE_TYPE,
1033 type_get_const(_base_type))
1037 # Emit OPCODE_MASK (if necessary) for a new mask value
1038 function srom_ops_emit_mask(opstream, mask, _prev_mask) {
1039 obj_assert_class(opstream, SromOpStream)
1040 _prev_mask = get(opstream, p_mask)
1042 if (_prev_mask == mask)
1045 set(opstream, p_mask, mask)
1046 srom_ops_emit_int_opcode(opstream,
1047 SPROM_OPCODE_MASK, SPROM_OPCODE_MASK_IMM,
1048 mask, sprintf("0x%x", mask))
1051 # Emit OPCODE_SHIFT (if necessary) for a new shift value
1052 function srom_ops_emit_shift(opstream, shift, _prev_shift) {
1053 obj_assert_class(opstream, SromOpStream)
1054 _prev_shift = get(opstream, p_shift)
1056 if (_prev_shift == shift)
1059 set(opstream, p_shift, shift)
1060 srom_ops_emit_int_opcode(opstream,
1061 SPROM_OPCODE_SHIFT, SPROM_OPCODE_SHIFT_IMM,
1065 # Return true if a valid BIND/BINDN encoding exists for the given SKIP_IN
1066 # value, false if the skip values exceed the limits of the bind opcode
1068 function srom_ops_can_encode_skip_in(skip_in) {
1069 return (skip_in >= SPROM_OP_BIND_SKIP_IN_MIN &&
1070 skip_in <= SPROM_OP_BIND_SKIP_IN_MAX)
1073 # Return true if a valid BIND/BINDN encoding exists for the given SKIP_OUT
1074 # value, false if the skip values exceed the limits of the bind opcode
1076 function srom_ops_can_encode_skip_out(skip_out) {
1077 return (skip_in >= SPROM_OP_BIND_SKIP_IN_MIN &&
1078 skip_in <= SPROM_OP_BIND_SKIP_IN_MAX)
1081 # Return true if a valid BIND/BINDN encoding exists for the given skip
1082 # values, false if the skip values exceed the limits of the bind opcode
1084 function srom_ops_can_encode_skip(skip_in, skip_out) {
1085 return (srom_ops_can_encode_skip_in(skip_in) &&
1086 srom_ops_can_encode_skip_out(skip_out))
1089 # Create a new SromOpBind instance for the given segment
1090 function srom_opbind_new(segment, skip_in, skip_out, _obj, _type, _width,
1093 obj_assert_class(segment, SromSegment)
1095 # Verify that an encoding exists for the skip values
1096 if (!srom_ops_can_encode_skip_in(skip_in)) {
1097 errorx(sprintf("cannot encode SKIP_IN=%d; maximum supported " \
1098 "range %d-%d", skip_in,
1099 SPROM_OP_BIND_SKIP_IN_MIN, SPROM_OP_BIND_SKIP_IN_MAX))
1102 if (!srom_ops_can_encode_skip_out(skip_out)) {
1103 errorx(sprintf("cannot encode SKIP_OUT=%d; maximum supported " \
1104 "range %d-%d", skip_out,
1105 SPROM_OP_BIND_SKIP_OUT_MIN, SPROM_OP_BIND_SKIP_OUT_MAX))
1108 # Fetch basic segment info
1109 _offset = get(segment, p_offset)
1110 _type = srom_segment_get_base_type(segment)
1111 _width = get(_type, p_width)
1113 # Construct new instance
1114 _obj = obj_new(SromOpBind)
1116 set(_obj, p_segment, segment)
1117 set(_obj, p_count, 1)
1118 set(_obj, p_offset, _offset)
1119 set(_obj, p_width, _width)
1120 set(_obj, p_skip_in, skip_in)
1121 set(_obj, p_skip_out, skip_out)
1122 set(_obj, p_buffer, array_new())
1127 # Try to coalesce a BIND for the given segment with an existing bind request,
1128 # returning true on success, or false if the two segments cannot be coalesced
1129 # into the existing request
1130 function srom_opbind_append(bind, segment, skip_out, _bind_seg, _bind_off,
1131 _width, _count, _skip_in, _seg_offset, _delta)
1133 obj_assert_class(bind, SromOpBind)
1134 obj_assert_class(segment, SromSegment)
1136 # Are the segments compatible?
1137 _bind_seg = get(bind, p_segment)
1138 if (!srom_segment_attributes_equal(_bind_seg, segment))
1141 # Are the output skip values compatible?
1142 if (get(bind, p_skip_out) != skip_out)
1145 # Find bind offset/count/width/skip
1146 _bind_off = get(bind, p_offset)
1147 _count = get(bind, p_count)
1148 _skip_in = get(bind, p_skip_in)
1149 _width = get(bind, p_width)
1151 # Fetch new segment's offset
1152 _seg_offset = get(segment, p_offset)
1154 # If there's only one segment in the bind op, we ned to compute the
1155 # skip value to be used for all later segments (including the
1156 # segment we're attempting to append)
1158 # If there's already multiple segments, we just need to verify that
1159 # the bind_offset + (count * width * skip_in) produces the new
1162 # Determine the delta between the two segment offsets. This
1163 # must be a multiple of the type width to be encoded
1165 _delta = _seg_offset - _bind_off
1166 if ((_delta % _width) != 0)
1169 # The skip byte count is calculated as (type width * skip)
1170 _skip_in = _delta / _width
1172 # Is the skip encodable?
1173 if (!srom_ops_can_encode_skip_in(_skip_in))
1176 # Save required skip
1177 set(bind, p_skip_in, _skip_in)
1178 } else if (_count > 1) {
1179 # Get the final offset of the binding if we were to add
1180 # one additional segment
1181 _bind_off = _bind_off + (_width * _skip_in * (_count + 1))
1183 # If it doesn't match our segment's offset, we can't
1184 # append this segment
1185 if (_bind_off != _seg_offset)
1189 # Success! Increment the bind count in the existing bind
1190 set(bind, p_count, _count + 1)
1194 # Return true if the given binding operation can be omitted from the output
1195 # if it would be immediately followed by a VAR, VAR_REL_IMM, or EOF opcode.
1197 # The bind operatin must be configured with default count, skip_in, and
1198 # skip_out values of 1, and must contain no buffered post-BIND opcodes
1199 function srom_opbind_is_implicit_encodable(bind) {
1200 obj_assert_class(bind, SromOpBind)
1202 if (get(bind, p_count) != 1)
1205 if (get(bind, p_skip_in) != 1)
1208 if (get(bind, p_skip_out) != 1)
1211 if (array_size(get(bind, p_buffer)) != 0)
1218 # Encode all segment settings for a single offset segment, followed by a bind
1221 # opstream: Opcode stream
1222 # segment: Segment to be written
1223 # continued: If this segment's value should be OR'd with the value of a
1225 function srom_ops_emit_segment(opstream, segment, continued, _value,
1226 _bind, _skip_in, _skip_out)
1228 obj_assert_class(opstream, SromOpStream)
1229 obj_assert_class(segment, SromSegment)
1231 # Determine basic bind parameters
1234 _skip_out = continued ? 0 : 1
1236 # Try to coalesce with a pending binding
1237 if ((_bind = get(opstream, p_pending_bind)) != null) {
1238 if (srom_opbind_append(_bind, segment, _skip_out))
1242 # Otherwise, flush any pending bind and enqueue our own
1243 srom_ops_flush_bind(opstream, 0)
1244 if (get(opstream, p_pending_bind))
1245 errorx("bind not flushed!")
1248 _value = get(segment, p_type)
1249 srom_ops_emit_type(opstream, _value)
1252 _value = get(segment, p_offset)
1253 srom_ops_emit_offset(opstream, _value)
1256 _value = get(segment, p_mask)
1257 srom_ops_emit_mask(opstream, _value)
1260 _value = get(segment, p_shift)
1261 srom_ops_emit_shift(opstream, _value)
1263 # Enqueue binding with opstream
1264 _bind = srom_opbind_new(segment, _skip_in, _skip_out)
1265 set(opstream, p_pending_bind, _bind)
1268 # (private) Adjust the stream's input offset by applying the given bind
1269 # operation's skip_in * width * count.
1270 function _srom_ops_apply_bind_offset(opstream, bind, _count, _offset, _width,
1271 _skip_in, _opstream_offset)
1273 obj_assert_class(opstream, SromOpStream)
1274 obj_assert_class(bind, SromOpBind)
1276 _opstream_offset = get(opstream, p_offset)
1277 _offset = get(bind, p_offset)
1278 if (_opstream_offset != _offset)
1279 errorx("stream/bind offset state mismatch")
1281 _count = get(bind, p_count)
1282 _width = get(bind, p_width)
1283 _skip_in = get(bind, p_skip_in)
1285 set(opstream, p_offset,
1286 _opstream_offset + ((_width * _skip_in) * _count))
1289 # (private) Write a bind instance and all buffered opcodes
1290 function _srom_ops_emit_bind(opstream, bind, _count, _skip_in, _skip_out,
1291 _off_start, _width, _si_signbit, _written, _nbuffer, _buffer)
1293 obj_assert_class(opstream, SromOpStream)
1294 obj_assert_class(bind, SromOpBind)
1296 # Assert that any pending bind state has already been cleared
1297 if (get(opstream, p_pending_bind) != null)
1298 errorx("cannot flush bind with an existing pending_bind active")
1300 # Fetch (and assert valid) our skip values
1301 _skip_in = get(bind, p_skip_in)
1302 _skip_out = get(bind, p_skip_out)
1304 if (!srom_ops_can_encode_skip(_skip_in, _skip_out))
1305 errorx("invalid skip values in buffered bind")
1307 # Determine SKIP_IN sign bit
1310 _si_signbit = SPROM_OP_BIND_SKIP_IN_SIGN
1312 # Emit BIND/BINDN opcodes until the full count is encoded
1313 _count = get(bind, p_count)
1314 while (_count > 0) {
1315 if (_count > 1 && _count <= SPROM_OP_IMM_MAX &&
1316 _skip_in == 1 && _skip_out == 1)
1318 # The one-byte BINDN form requires encoding the count
1319 # as a IMM, and has an implicit in/out skip of 1.
1320 srom_ops_emit_opcode(opstream,
1321 "("SPROM_OPCODE_DO_BINDN_IMM"|"_count")")
1324 } else if (_count > 1) {
1325 # The two byte BINDN form can encode skip values and a
1327 _written = min(_count, UInt8Max)
1329 srom_ops_emit_opcode(opstream,
1330 sprintf("(%s|%s|(%u<<%s)|(%u<<%s))",
1331 SPROM_OPCODE_DO_BINDN,
1333 abs(_skip_in), SPROM_OP_BIND_SKIP_IN_SHIFT,
1334 _skip_out, SPROM_OP_BIND_SKIP_OUT_SHIFT),
1339 # The 1-byte BIND form can encode the same SKIP values
1340 # as the 2-byte BINDN, with a implicit count of 1
1341 srom_ops_emit_opcode(opstream,
1342 sprintf("(%s|%s|(%u<<%s)|(%u<<%s))",
1343 SPROM_OPCODE_DO_BIND,
1345 abs(_skip_in), SPROM_OP_BIND_SKIP_IN_SHIFT,
1346 _skip_out, SPROM_OP_BIND_SKIP_OUT_SHIFT))
1351 # Update the stream's input offset
1352 _srom_ops_apply_bind_offset(opstream, bind)
1354 # Write any buffered post-BIND opcodes
1355 _buffer = get(bind, p_buffer)
1356 _nbuffer = array_size(_buffer)
1357 for (_i = 0; _i < _nbuffer; _i++)
1358 srom_ops_emit(opstream, array_get(_buffer, _i))
1361 # Flush any buffered binding
1362 function srom_ops_flush_bind(opstream, allow_implicit, _bind, _bind_total)
1364 obj_assert_class(opstream, SromOpStream)
1366 # If no pending bind, nothing to flush
1367 if ((_bind = get(opstream, p_pending_bind)) == null)
1370 # Check the per-variable bind count to determine whether
1371 # we can encode an implicit bind.
1373 # If there have been any explicit bind statements, implicit binding
1375 _bind_total = get(opstream, p_bind_total)
1376 if (allow_implicit && _bind_total > 0) {
1377 # Disable implicit encoding; explicit bind statements have
1378 # been issued for this variable previously.
1382 # Increment bind count
1383 set(opstream, p_bind_total, _bind_total + 1)
1385 # Clear the property value
1386 set(opstream, p_pending_bind, null)
1388 # If a pending bind operation can be encoded as an implicit bind,
1389 # emit a descriptive comment and update the stream state.
1391 # Otherwise, emit the full set of bind opcode(s)
1392 _base_off = get(opstream, p_offset)
1393 if (allow_implicit && srom_opbind_is_implicit_encodable(_bind)) {
1394 # Update stream's input offset
1395 _srom_ops_apply_bind_offset(opstream, _bind)
1397 _srom_ops_emit_bind(opstream, _bind)
1400 # Provide bind information as a comment
1401 srom_ops_emit(opstream,
1402 sprintf("/* bind (%s @ %#x -> %#x) */\n",
1403 type_to_string(get(opstream, p_type)),
1404 _base_off, get(opstream, p_offset)))
1410 # Write OPCODE_EOF after flushing any buffered writes
1411 function srom_ops_emit_eof(opstream) {
1412 obj_assert_class(opstream, SromOpStream)
1414 # Flush any buffered writes
1415 srom_ops_flush_bind(opstream, 1)
1417 # Emit an explicit VAR_END opcode for the last entry
1418 srom_ops_emit_opcode(opstream, SPROM_OPCODE_VAR_END)
1421 srom_ops_emit_opcode(opstream, SPROM_OPCODE_EOF)
1424 # Write the SROM offset segment bindings to the opstream
1425 function write_srom_offset_bindings(opstream, offsets,
1426 _noffsets, _offset, _segs, _nsegs, _segment, _cont,
1429 _noffsets = array_size(offsets)
1430 for (_i = 0; _i < _noffsets; _i++) {
1431 # Encode each segment in this offset
1432 _offset = array_get(offsets, _i)
1433 _segs = get(_offset, p_segments)
1434 _nsegs = array_size(_segs)
1436 for (_j = 0; _j < _nsegs; _j++) {
1437 _segment = array_get(_segs, _j)
1440 # Should this value be OR'd with the next segment?
1445 srom_ops_emit_segment(opstream, _segment, _cont)
1450 # Write the SROM entry stream for a SROM entry to the output file
1451 function write_srom_entry_bindings(entry, opstream, _var, _vid,
1452 _var_type, _entry_type, _offsets, _noffsets)
1454 _var = get(entry, p_var)
1455 _vid = get(_var, p_vid)
1457 # Encode revision switch. This resets variable state, so must
1458 # occur before any variable definitions to which it applies
1459 srom_ops_emit_revisions(opstream, get(entry, p_revisions))
1461 # Encode variable ID
1462 srom_ops_reset_var(opstream, _var, _vid)
1465 # Write entry-specific array length (SROM layouts may define array
1466 # mappings with fewer elements than in the variable definition)
1467 if (srom_entry_has_array_type(entry)) {
1468 _var_type = get(_var, p_type)
1469 _entry_type = get(entry, p_type)
1471 # If the array length differs from the variable default,
1472 # write an OPCODE_EXT_NELEM entry
1473 if (type_get_nelem(_var_type) != type_get_nelem(_entry_type)) {
1474 srom_ops_emit_opcode(opstream, SPROM_OPCODE_NELEM,
1475 srom_entry_get_array_len(entry))
1479 # Write offset segment bindings
1480 _offsets = get(entry, p_offsets)
1481 write_srom_offset_bindings(opstream, _offsets)
1485 # Write a SROM layout binding opcode table to the output file
1486 function write_srom_bindings(layout, _varname, _var, _all_entries,
1487 _nall_entries, _entries, _nentries, _entry, _opstream, _i)
1489 _varname = srom_layout_get_variable_name(layout)
1490 _all_entries = get(layout, p_entries)
1491 _opstream = srom_ops_new(layout)
1494 # Collect all entries to be included in the output, and then
1495 # sort by their variable's assigned ID (ascending).
1497 # The variable IDs were previously assigned in lexigraphical sort
1498 # order; since the variable *offsets* tend to match this order, this
1499 # works out well for our compact encoding, allowing us to make use of
1500 # compact relative encoding of both variable IDs and variable offsets.
1502 _entries = array_new()
1503 _nall_entries = array_size(_all_entries)
1504 for (_i = 0; _i < _nall_entries; _i++) {
1505 _entry = array_get(_all_entries, _i)
1506 _var = get(_entry, p_var)
1508 # Skip internal variables
1509 if (var_is_internal(_var))
1512 # Sanity check variable ID assignment
1513 if (get(_var, p_vid) == "")
1514 errorx("missing variable ID for " obj_to_string(_var))
1516 array_append(_entries, _entry)
1519 # Sort entries by (variable ID, revision range), ascending
1520 array_sort(_entries, prop_path_create(p_var, p_vid),
1521 prop_path_create(p_revisions, p_start),
1522 prop_path_create(p_revisions, p_end))
1524 # Emit all entry binding opcodes
1525 emit("static const uint8_t " _varname "[] = {\n")
1528 _nentries = array_size(_entries)
1529 for (_i = 0; _i < _nentries; _i++) {
1530 _entry = array_get(_entries, _i)
1531 write_srom_entry_bindings(_entry, _opstream)
1534 # Flush and write EOF
1535 srom_ops_emit_eof(_opstream)
1540 obj_delete(_opstream)
1541 obj_delete(_entries)
1544 # Write the BHND_NVAR_<NAME>_ID #defines to the output file
1545 function write_data_defines(output_vars, _noutput_vars, _tab_align, _var,
1546 _macro, _macros, _num_macros, _i)
1548 # Produce our array of #defines
1550 _noutput_vars = array_size(output_vars)
1551 for (_i = 0; _i < _noutput_vars; _i++) {
1552 _var = array_get(output_vars, _i)
1555 _macro = var_get_macro(_var, MTypeVarID, get(_var, p_vid))
1556 _macros[_num_macros++] = _macro
1559 # Calculate value tab alignment position for our macros
1560 _tab_align = macros_get_tab_alignment(_macros, _num_macros)
1562 # Write the #defines
1563 emit("/* ID constants provide an index into the variable array */\n")
1564 for (_i = 0; _i < _num_macros; _i++)
1565 write_macro_define(_macros[_i], _tab_align)
1569 # Calculate the common tab alignment to be used with a set of prefix strings
1570 # with the given maximum length
1571 function tab_alignment(max_len, _tab_align) {
1572 _tab_align = max_len
1573 _tab_align += (TAB_WIDTH - (_tab_align % TAB_WIDTH)) % TAB_WIDTH
1574 _tab_align /= TAB_WIDTH
1579 # Generate and return a tab string that can be appended to a string of
1580 # `strlen` to pad the column out to `align_to`
1582 # Note: If the string from which strlen was derived contains tabs, the result
1584 function tab_str(strlen, align_to, _lead, _pad, _result, _i) {
1586 _lead -= (_lead % TAB_WIDTH);
1589 # Determine required padding to reach the desired alignment
1590 if (align_to >= _lead)
1591 _pad = align_to - _lead;
1595 for (_i = 0; _i < _pad; _i++)
1596 _result = _result "\t"
1602 # Write a MacroDefine constant, padding the constant out to `align_to`
1603 function write_macro_define(macro, align_to, _tabstr, _i) {
1604 # Determine required padding to reach the desired alignment
1605 _tabstr = tab_str(length(get(macro, p_name)), align_to)
1607 emit("#define\t" get(macro, p_name) _tabstr get(macro, p_value) "\n")
1610 # Calculate the tab alignment to be used with a given integer-indexed array
1611 # of Macro instances.
1612 function macros_get_tab_alignment(macros, macros_len, _macro, _max_len, _i) {
1614 for (_i = 0; _i < macros_len; _i++) {
1616 _max_len = max(_max_len, length(get(_macro, p_name)))
1619 return (tab_alignment(_max_len))
1622 # Variable group block
1623 $1 == "group" && in_parser_context(NVRAM) {
1624 parse_variable_group()
1627 # Variable definition
1628 (($1 ~ VACCESS_REGEX && $2 ~ TYPES_REGEX) || $1 ~ TYPES_REGEX) &&
1629 in_parser_context(SymbolContext) \
1631 parse_variable_defn()
1634 # Variable "fmt" parameter
1635 $1 == "fmt" && in_parser_context(Var) {
1636 parse_variable_param($1)
1640 # Variable "all1" parameter
1641 $1 == "all1" && in_parser_context(Var) {
1642 parse_variable_param($1)
1646 # Variable desc/help parameters
1647 ($1 == "desc" || $1 == "help") && in_parser_context(Var) {
1648 parse_variable_param($1)
1653 $1 == "srom" && in_parser_context(NVRAM) {
1658 # SROM layout revision filter block
1659 $1 == "srom" && in_parser_context(SromLayout) {
1660 parse_srom_layout_filter()
1663 # SROM layout variable entry
1664 $1 ~ "("OFF_REGEX"):$" && \
1665 (in_parser_context(SromLayout) || in_parser_context(SromLayoutFilter)) \
1667 parse_srom_variable_entry()
1671 # SROM entry segment
1672 $1 ~ "("REL_OFF_REGEX"|"OFF_REGEX")[:,|]?" && in_parser_context(SromEntry) {
1673 parse_srom_entry_segments()
1676 # Skip comments and blank lines
1677 /^[ \t]*#/ || /^$/ {
1682 /}/ && !in_parser_context(NVRAM) {
1683 while (!in_parser_context(NVRAM) && $0 ~ "}") {
1684 parser_state_close_block();
1689 # Report unbalanced '}'
1690 /}/ && in_parser_context(NVRAM) {
1694 # Invalid variable type
1695 $1 && in_parser_context(SymbolContext) {
1696 error("unknown type '" $1 "'")
1699 # Generic parse failure
1701 error("unrecognized statement")
1704 # Create a class instance with the given name
1705 function class_new(name, superclass, _class) {
1707 errorx("class_get() must be called with one or two arguments")
1709 # Look for an existing class instance
1710 if (name in _g_class_names)
1711 errorx("redefining class: " name)
1713 # Create and register the class object
1714 _class = obj_new(superclass)
1715 _g_class_names[name] = _class
1716 _g_obj[_class,OBJ_IS_CLS] = 1
1717 _g_obj[_class,CLS_NAME] = name
1722 # Return the class instance with the given name
1723 function class_get(name) {
1724 if (name in _g_class_names)
1725 return (_g_class_names[name])
1727 errorx("no such class " name)
1730 # Return the name of cls
1731 function class_get_name(cls) {
1733 warnx("class_get_name() called with null class")
1737 if (!obj_is_class(cls))
1738 errorx(cls " is not a class object")
1740 return (_g_obj[cls,CLS_NAME])
1743 # Return true if the given property property ID is defined on class
1744 function class_has_prop_id(class, prop_id, _super) {
1746 errorx("class_has_prop_id() must be called with two arguments")
1751 if (prop_id == null)
1754 # Check class<->prop cache
1755 if ((class, prop_id) in _g_class_prop_cache)
1758 # Otherwise, slow path
1759 if (!obj_is_class(class))
1760 errorx(class " is not a class object")
1763 errorx("class_has_prop_id() must be called with two arguments")
1765 for (_super = class; _super != null; _super = obj_get_class(_super)) {
1766 if (!((_super,CLS_PROP,prop_id) in _g_obj))
1769 # Found; add to class<->prop cache
1770 _g_class_prop_cache[class,prop_id] = 1
1777 # Return true if the given property prop is defined on class
1778 function class_has_property(class, prop) {
1779 if (!(PROP_ID in prop))
1782 return (class_has_prop_id(class, prop[PROP_ID]))
1785 # Define a `prop` on `class` with the given `name` string
1786 function class_add_prop(class, prop, name, _prop_id) {
1787 if (_prop_id != null)
1788 errorx("class_add_prop() must be called with three arguments")
1790 # Check for duplicate property definition
1791 if (class_has_property(class, prop))
1792 errorx("property " prop[PROP_NAME] " already defined on " \
1793 class_get_name(class))
1796 if (_g_prop_ids == null)
1799 # Get (or create) new property entry
1800 if (name in _g_prop_names) {
1801 _prop_id = _g_prop_names[name]
1803 _prop_id = _g_prop_ids++
1804 _g_prop_names[name] = _prop_id
1805 _g_props[_prop_id] = name
1807 prop[PROP_NAME] = name
1808 prop[PROP_ID] = _prop_id
1811 # Add to class definition
1812 _g_obj[class,CLS_PROP,prop[PROP_ID]] = name
1816 # Return the property ID for a given class-defined property
1817 function class_get_prop_id(class, prop) {
1819 errorx("class_get_prop_id() on null class")
1821 if (!class_has_property(class, prop)) {
1822 errorx("requested undefined property '" prop[PROP_NAME] "on " \
1823 class_get_name(class))
1826 return (prop[PROP_ID])
1829 # Return the property ID for a given class-defined property name
1830 function class_get_named_prop_id(class, name, _prop_id) {
1832 errorx("class_get_prop_id() on null class")
1834 if (!(name in _g_prop_names))
1835 errorx("requested undefined property '" name "'")
1837 _prop_id = _g_prop_names[name]
1839 if (!class_has_prop_id(class, _prop_id)) {
1840 errorx("requested undefined property '" _g_props[_prop_id] \
1841 "' on " class_get_name(class))
1847 # Create a new instance of the given class
1848 function obj_new(class, _obj) {
1850 errorx("obj_new() must be called with one argument")
1852 if (_g_obj_ids == null)
1855 # Assign ID and set superclass
1857 _g_obj[_obj,OBJ_SUPER] = class
1862 # obj_delete() support for Map instances
1863 function _obj_delete_map(obj, _prefix, _key) {
1864 obj_assert_class(obj, Map)
1865 _prefix = "^" obj SUBSEP
1866 for (_key in _g_maps) {
1867 if (!match(_key, _prefix) && _key != obj)
1869 delete _g_maps[_key]
1873 # obj_delete() support for Array instances
1874 function _obj_delete_array(obj, _size, _i) {
1875 obj_assert_class(obj, Array)
1876 _size = array_size(obj)
1878 for (_i = 0; _i < _size; _i++)
1879 delete _g_arrays[obj,OBJ_PROP,_i]
1882 # Destroy all metadata associated with the given object
1883 function obj_delete(obj, _prop_id, _prop_name, _prefix, _key, _size, _i) {
1884 if (obj_is_class(obj))
1885 errorx("cannot delete class objects")
1887 # Handle classes that use external global array storage
1889 if (obj_is_instanceof(obj, Map)) {
1890 _obj_delete_map(obj)
1891 } else if (obj_is_instanceof(obj, Array)) {
1892 _obj_delete_array(obj)
1895 # Delete all object properties
1896 for (_prop_name in _g_prop_names) {
1897 if (!obj_has_prop_id(obj, _prop_id))
1900 _prop_id = _g_prop_names[_prop_name]
1901 delete _g_obj[obj,OBJ_PROP,_prop_id]
1902 delete _g_obj_nr[obj,OBJ_PROP,_prop_id]
1905 # Delete instance state
1906 delete _g_obj[obj,OBJ_IS_CLS]
1907 delete _g_obj[obj,OBJ_SUPER]
1910 # Print an object's unique ID, class, and properties to
1912 function obj_dump(obj, _pname, _prop_id, _prop_val) {
1913 print(class_get_name(obj_get_class(obj)) "<" obj ">:")
1915 # Dump all properties
1916 for (_pname in _g_prop_names) {
1917 _prop_id = _g_prop_names[_pname]
1919 if (!obj_has_prop_id(obj, _prop_id))
1922 _prop_val = prop_get(obj, _prop_id)
1923 printf("\t%s: %s\n", _pname, _prop_val)
1927 # Return true if obj is a class object
1928 function obj_is_class(obj) {
1929 return (_g_obj[obj,OBJ_IS_CLS] == 1)
1932 # Return the class of obj, if any.
1933 function obj_get_class(obj) {
1935 errorx("obj_get_class() on null object")
1936 return (_g_obj[obj,OBJ_SUPER])
1939 # Return true if obj is an instance of the given class
1940 function obj_is_instanceof(obj, class, _super) {
1942 errorx("obj_is_instanceof() must be called with two arguments")
1944 if (!obj_is_class(class))
1945 errorx(class " is not a class object")
1948 errorx("obj_is_instanceof() called with null obj (class " \
1949 class_get_name(class) ")")
1952 for (_super = obj_get_class(obj); _super != null;
1953 _super = obj_get_class(_super))
1955 if (_super == class)
1962 # Default object shallow equality implementation. Returns true if the two
1963 # objects share a common superclass and have identity equality across all defined
1965 function obj_trivially_equal(lhs, rhs, _class, _pname, _prop_id) {
1970 # Must share a common superclass
1971 _class = obj_get_class(lhs)
1972 if (_class != obj_get_class(rhs))
1975 # Compare all properties
1977 for (_pname in _g_prop_names) {
1978 _prop_id = _g_prop_names[_pname]
1980 if (!class_has_prop_id(_class, _prop_id))
1983 if (prop_get(lhs, _prop_id) != prop_get(rhs, _prop_id))
1987 # All properties are trivially equal
1992 # Return a debug string representation of an object's unique ID, class, and
1994 function obj_to_string(obj, _pname, _prop_id, _prop_val, _prop_count, _result) {
1995 _result = class_get_name(obj_get_class(obj)) "<" obj ">: { "
1997 # Fetch all properties
1999 for (_pname in _g_prop_names) {
2000 _prop_id = _g_prop_names[_pname]
2002 if (!obj_has_prop_id(obj, _prop_id))
2005 if (_prop_count >= 0)
2006 _result = _result ", "
2008 _result = _result sprintf("\t%s: %s\n", _pname, _prop_val)
2012 return (_result " }")
2015 # Assert that obj is an instance of the given class
2016 function obj_assert_class(obj, class) {
2017 if (!obj_is_instanceof(obj, class)) {
2018 errorx(class_get_name(obj_get_class(obj)) "<" obj "> is not " \
2019 "an instance of " class_get_name(class))
2023 # Return true if the given property prop is defined by the object's superclass
2024 function obj_has_property(obj, prop, _class) {
2026 errorx("obj_has_property() on null object")
2028 _class = obj_get_class(obj)
2029 return (class_has_property(_class, prop))
2032 # Return true if the given property ID is defined by the object's superclass
2033 function obj_has_prop_id(obj, prop_id, _class) {
2035 errorx("obj_has_prop_id() on null object")
2037 _class = obj_get_class(obj)
2038 return (class_has_prop_id(_class, prop_id))
2041 # Return the line (NR) at which a given property ID was set on the object
2042 # Will throw an error if the property has not been set on obj
2043 function obj_get_prop_id_nr(obj, prop_id) {
2045 errorx("obj_get_prop_id_nr() on null object")
2047 if (!obj_has_prop_id(obj, prop_id)) {
2048 errorx("requested undefined property '" _g_props[prop_id] \
2049 "' (" prop_id ") on " obj_to_string(obj))
2053 if ((obj,OBJ_PROP,prop_id) in _g_obj_nr)
2054 return (_g_obj_nr[obj,OBJ_PROP,prop_id])
2056 errorx("property '" _g_props[prop_id] "' (" prop_id ") not " \
2057 "previously set on " obj_to_string(obj))
2060 # Return the line (NR) at which a given property was set on the object
2061 # Will throw an error if the property has not been set on obj
2062 function obj_get_prop_nr(obj, prop) {
2063 return (obj_get_prop_id_nr(obj, prop[PROP_ID]))
2066 # Return an abstract property ID for a given property
2067 function obj_get_prop_id(obj, prop) {
2069 errorx("obj_get_prop_id() on null object")
2071 return (class_get_prop_id(obj_get_class(obj), prop))
2075 # Return the property ID for a given property name
2076 function obj_get_named_prop_id(obj, name) {
2078 errorx("obj_get_named_prop_id() on null object")
2080 return (class_get_named_prop_id(obj_get_class(obj), name))
2083 # Set a property on obj
2084 function set(obj, prop, value, _class) {
2085 return (prop_set(obj, prop[PROP_ID], value))
2088 # Get a property value defined on obj
2089 function get(obj, prop, _class) {
2090 return (prop_get(obj, prop[PROP_ID]))
2093 # Set a property on obj, using a property ID returned by obj_get_prop_id() or
2094 # class_get_prop_id()
2095 function prop_set(obj, prop_id, value, _class) {
2097 errorx("setting property '" _g_props[prop_id] \
2101 _class = obj_get_class(obj)
2103 errorx(obj " has no superclass")
2105 if (!class_has_prop_id(_class, prop_id)) {
2106 errorx("requested undefined property '" _g_props[prop_id] \
2107 "' (" prop_id ") on " class_get_name(_class))
2110 # Track the line on which the property was set
2111 _g_obj_nr[obj,OBJ_PROP,prop_id] = NR
2112 _g_obj[obj,OBJ_PROP,prop_id] = value
2115 # Convert a property ID to a property path.
2116 function prop_id_to_path(prop_id) {
2117 if (!(prop_id in _g_props))
2118 errorx("'" prop_id "' is not a property ID")
2120 # Convert to path string representation
2124 # Convert a property to a property path.
2125 function prop_to_path(prop) {
2126 if (!(PROP_ID in prop))
2127 errorx("prop_to_path() called with non-property head")
2129 return (prop_id_to_path(prop[PROP_ID]))
2132 # Create a property path from head and tail properties
2133 # Additional properties may be appended via prop_path_append() or
2134 # prop_path_append_id()
2135 function prop_path_create(head, tail) {
2136 if (!(PROP_ID in head))
2137 errorx("prop_path() called with non-property head")
2139 if (!(PROP_ID in tail))
2140 errorx("prop_path() called with non-property tail")
2142 return (head[PROP_ID] SUBSEP tail[PROP_ID])
2145 # Append a property to the given property path
2146 function prop_path_append(path, tail) {
2147 if (!(PROP_ID in tail))
2148 errorx("prop_path_append() called with non-property tail")
2150 return (prop_path_append_id(path, tail[PROP_ID]))
2153 # Append a property ID to the given property path
2154 function prop_path_append_id(path, tail_id) {
2155 if (!(tail_id in _g_props))
2156 errorx("'" tail_id "' is not a property ID")
2158 return (path SUBSEP tail_id)
2161 # Fetch a value from obj using a property path previously returned by
2162 # prop_path_create(), prop_to_path(), etc.
2163 function prop_get_path(obj, prop_path, _class, _prop_ids, _nprop_ids, _next,
2164 _prop_head, _prop_len, _prop_tail)
2167 errorx("requested property path '" \
2168 gsub(SUBSEP, ".", prop_path) "' on null object")
2171 # Try the cache first
2172 _class = obj_get_class(obj)
2173 if ((_class,prop_path,PPATH_HEAD) in _g_ppath_cache) {
2174 _prop_head = _g_ppath_cache[_class,prop_path,PPATH_HEAD]
2175 _next = prop_get(obj, _prop_head)
2177 if ((_class,prop_path,PPATH_TAIL) in _g_ppath_cache) {
2178 _prop_tail = _g_ppath_cache[_class,prop_path,PPATH_TAIL]
2179 return (prop_get_path(_next, _prop_tail))
2185 # Parse the head/tail of the property path and add to cache
2186 _nprop_ids = split(prop_path, _prop_ids, SUBSEP)
2187 if (_nprop_ids == 0)
2188 errorx("empty property path")
2189 _prop_head = _prop_ids[1]
2190 _g_ppath_cache[_class,prop_path,PPATH_HEAD] = _prop_head
2192 if (_nprop_ids > 1) {
2193 _prop_len = length(_prop_head)
2194 _prop_tail = substr(prop_path, _prop_len+2)
2197 _g_ppath_cache[_class,prop_path,PPATH_TAIL] = _prop_tail
2200 # Recursively call out implementation, this time fetching from
2202 return (prop_get_path(obj, prop_path))
2205 # Fetch a value property value from obj, using a property ID returned by
2206 # obj_get_prop_id() or class_get_prop_id()
2207 function prop_get(obj, prop_id, _class) {
2209 errorx("requested property '" _g_props[prop_id] \
2213 _class = obj_get_class(obj)
2215 errorx(obj " has no superclass")
2217 if (!class_has_prop_id(_class, prop_id)) {
2218 errorx("requested undefined property '" _g_props[prop_id] \
2219 "' (" prop_id ") on " class_get_name(_class))
2222 return (_g_obj[obj,OBJ_PROP,prop_id])
2225 # Create a new MacroType instance
2226 function macro_type_new(name, const_suffix, _obj) {
2227 _obj = obj_new(MacroType)
2229 set(_obj, p_name, name)
2230 set(_obj, p_const_suffix, const_suffix)
2235 # Create a new MacroDefine instance
2236 function macro_new(name, value, _obj) {
2237 _obj = obj_new(MacroDefine)
2238 set(_obj, p_name, name)
2239 set(_obj, p_value, value)
2244 # Create an empty array; this uses _g_arrays to store integer
2245 # keys/values under the object's property prefix.
2246 function array_new(_obj) {
2247 _obj = obj_new(Array)
2248 set(_obj, p_count, 0)
2253 # Return the number of elements in the array
2254 function array_size(array) {
2255 obj_assert_class(array, Array)
2256 return (get(array, p_count))
2259 # Return true if the array is empty
2260 function array_empty(array) {
2261 return (array_size(array) == 0)
2264 # Append a value to the array
2265 function array_append(array, value, _i) {
2266 obj_assert_class(array, Array)
2268 _i = get(array, p_count)
2269 _g_arrays[array,OBJ_PROP,_i] = value
2270 set(array, p_count, _i+1)
2273 # Set an array value
2274 # An error will be thrown if the idx is outside array bounds
2275 function array_set(array, idx, value) {
2276 obj_assert_class(array, Array)
2278 if (!((array,OBJ_PROP,idx) in _g_arrays))
2279 errorx(idx " out of range of array " obj_to_string(array))
2281 _g_arrays[array,OBJ_PROP,idx] = value
2284 # Return value at the given index from the array
2285 # An error will be thrown if 'idx' is outside the array bounds
2286 function array_get(array, idx) {
2287 obj_assert_class(array, Array)
2289 if (!((array,OBJ_PROP,idx) in _g_arrays))
2290 errorx(idx " out of range of array " obj_to_string(array))
2292 return (_g_arrays[array,OBJ_PROP,idx])
2297 # Sort an array, using standard awk comparison operators over its values.
2299 # If `prop_path*` is non-NULL, the corresponding property path (or property ID)
2300 # will be fetched from each array element and used as the sorting value.
2302 # If multiple property paths are specified, the array is first sorted by
2303 # the first path, and then any equal values are sorted by the second path,
2306 function array_sort(array, prop_path0, prop_path1, prop_path2, _size) {
2307 obj_assert_class(array, Array)
2310 errorx("no more than three property paths may be specified")
2312 _size = array_size(array)
2316 _qsort(array, prop_path0, prop_path1, prop_path2, 0, _size-1)
2319 function _qsort_get_key(array, idx, prop_path, _v) {
2320 _v = array_get(array, idx)
2322 if (prop_path == null)
2325 return (prop_get_path(_v, prop_path))
2328 function _qsort_compare(array, lhs_idx, rhs_val, ppath0, ppath1, ppath2,
2329 _lhs_val, _rhs_prop_val)
2331 _lhs_val = _qsort_get_key(array, lhs_idx, ppath0)
2333 _rhs_prop_val = rhs_val
2335 _rhs_prop_val = prop_get_path(rhs_val, ppath0)
2337 if (_lhs_val == _rhs_prop_val && ppath1 != null) {
2338 _lhs_val = _qsort_get_key(array, lhs_idx, ppath1)
2339 _rhs_prop_val = prop_get_path(rhs_val, ppath1)
2341 if (_lhs_val == _rhs_prop_val && ppath2 != null) {
2342 _lhs_val = _qsort_get_key(array, lhs_idx, ppath2)
2343 _rhs_prop_val = prop_get_path(rhs_val, ppath2)
2347 if (_lhs_val < _rhs_prop_val)
2349 else if (_lhs_val > _rhs_prop_val)
2355 function _qsort(array, ppath0, ppath1, ppath2, first, last, _qpivot,
2356 _qleft, _qleft_val, _qright, _qright_val)
2361 # select pivot element
2362 _qpivot = int(first + int((last-first+1) * rand()))
2366 _qpivot_val = array_get(array, _qpivot)
2369 while (_qleft <= _qright) {
2370 while (_qsort_compare(array, _qleft, _qpivot_val, ppath0, ppath1,
2376 while (_qsort_compare(array, _qright, _qpivot_val, ppath0, ppath1,
2383 if (_qleft <= _qright) {
2384 _qleft_val = array_get(array, _qleft)
2385 _qright_val = array_get(array, _qright)
2387 array_set(array, _qleft, _qright_val)
2388 array_set(array, _qright, _qleft_val)
2395 # sort the partitions
2396 _qsort(array, ppath0, ppath1, ppath2, first, _qright)
2397 _qsort(array, ppath0, ppath1, ppath2, _qleft, last)
2402 # Join all array values with the given separator
2404 # If `prop_path` is non-NULL, the corresponding property path (or property ID)
2405 # will be fetched from each array value and included in the result, rather than
2406 # immediate array value
2408 function array_join(array, sep, prop_path, _i, _size, _value, _result) {
2409 obj_assert_class(array, Array)
2412 _size = array_size(array)
2413 for (_i = 0; _i < _size; _i++) {
2414 # Fetch the value (and optionally, a target property)
2415 _value = array_get(array, _i)
2416 if (prop_path != null)
2417 _value = prop_get_path(_value, prop_path)
2420 _result = _result _value sep
2422 _result = _result _value
2428 # Return the first value in the array, or null if empty
2429 function array_first(array) {
2430 obj_assert_class(array, Array)
2432 if (array_size(array) == 0)
2435 return (array_get(array, 0))
2438 # Return the last value in the array, or null if empty
2439 function array_tail(list, _size) {
2440 obj_assert_class(array, Array)
2442 _size = array_size(array)
2446 return (array_get(array, _size-1))
2449 # Create an empty hash table; this uses the _g_maps array to store arbitrary
2450 # keys/values under the object's property prefix.
2451 function map_new(_obj) {
2456 # Add `key` with `value` to `map`
2457 function map_set(map, key, value) {
2458 obj_assert_class(map, Map)
2459 _g_maps[map,OBJ_PROP,key] = value
2462 # Remove `key` from the map
2463 function map_remove(map, key) {
2464 obj_assert_class(map, Map)
2465 delete _g_maps[map,OBJ_PROP,key]
2468 # Return true if `key` is found in `map`, false otherwise
2469 function map_contains(map, key) {
2470 obj_assert_class(map, Map)
2471 return ((map,OBJ_PROP,key) in _g_maps)
2474 # Fetch the value of `key` from the map. Will throw an error if the
2475 # key does not exist
2476 function map_get(map, key) {
2477 obj_assert_class(map, Map)
2478 return _g_maps[map,OBJ_PROP,key]
2481 # Create and return a new list containing all defined values in `map`
2482 function map_to_array(map, _key, _prefix, _values) {
2483 obj_assert_class(map, Map)
2485 _values = array_new()
2486 _prefix = "^" map SUBSEP OBJ_PROP SUBSEP
2487 for (_key in _g_maps) {
2488 if (!match(_key, _prefix))
2491 array_append(_values, _g_maps[_key])
2497 # Create a new Type instance
2498 function type_new(name, width, signed, constant, array_constant, fmt, mask,
2499 constant_value, array_constant_value, _obj)
2501 obj_assert_class(fmt, Fmt)
2503 _obj = obj_new(Type)
2504 set(_obj, p_name, name)
2505 set(_obj, p_width, width)
2506 set(_obj, p_signed, signed)
2507 set(_obj, p_const, constant)
2508 set(_obj, p_const_val, constant_value)
2509 set(_obj, p_array_const, array_constant)
2510 set(_obj, p_array_const_val, array_constant_value)
2511 set(_obj, p_default_fmt, fmt)
2512 set(_obj, p_mask, mask)
2517 # Return true if two types are equal
2518 function type_equal(lhs, rhs) {
2523 # Must share a common class
2524 if (obj_get_class(lhs) != obj_get_class(rhs))
2527 # Handle ArrayType equality
2528 if (obj_is_instanceof(lhs, ArrayType)) {
2529 # Size must be equal
2530 if (get(lhs, p_count) != get(rhs, p_count))
2533 # The base types must be equal
2534 return (type_equal(type_get_base(lhs), type_get_base(rhs)))
2537 # Handle Type equality -- we just check for trivial identity
2538 # equality of all members
2539 obj_assert_class(lhs, Type)
2540 return (obj_trivially_equal(lhs, rhs))
2543 # Return the type's default value mask. If the type is an array type,
2544 # the default mask of the base type will be returned.
2545 function type_get_default_mask(type) {
2546 if (obj_is_instanceof(type, ArrayType))
2547 return (type_get_default_mask(type_get_base(type)))
2549 obj_assert_class(type, Type)
2550 return (get(type, p_mask))
2553 # Return the type's C constant representation
2554 function type_get_const(type) {
2555 if (obj_is_instanceof(type, ArrayType))
2556 return (get(type_get_base(type), p_array_const))
2558 obj_assert_class(type, Type)
2559 return (get(type, p_const))
2562 # Return the type's C constant integer value
2563 function type_get_const_val(type) {
2564 if (obj_is_instanceof(type, ArrayType))
2565 return (get(type_get_base(type), p_array_const_val))
2567 obj_assert_class(type, Type)
2568 return (get(type, p_const_val))
2571 # Return an array type's element count, or 1 if the type is not
2573 function type_get_nelem(type) {
2574 if (obj_is_instanceof(type, ArrayType))
2575 return (get(type, p_count))
2577 obj_assert_class(type, Type)
2581 # Return the base type for a given type instance.
2582 function type_get_base(type) {
2583 if (obj_is_instanceof(type, ArrayType))
2584 return (type_get_base(get(type, p_type)))
2586 obj_assert_class(type, Type)
2590 # Return the default fmt for a given type instance
2591 function type_get_default_fmt(type, _base, _fmt, _array_fmt) {
2592 _base = type_get_base(type)
2593 _fmt = get(_base, p_default_fmt)
2595 if (obj_is_instanceof(type, ArrayType)) {
2596 _array_fmt = get(_fmt, p_array_fmt)
2597 if (_array_fmt != null)
2604 # Return a string representation of the given type
2605 function type_to_string(type, _base_type) {
2606 if (obj_is_instanceof(type, ArrayType)) {
2607 _base_type = type_get_base(type)
2608 return (type_to_string(_base_type) "[" get(type, p_count) "]")
2610 return get(type, p_name)
2613 # Return true if type `rhs` is can be coerced to type `lhs` without data
2615 function type_can_represent(lhs, rhs) {
2616 # Must be of the same class (Type or ArrayType)
2617 if (obj_get_class(lhs) != obj_get_class(rhs))
2620 if (obj_is_instanceof(lhs, ArrayType)) {
2621 # The base types must have a representable relationship
2622 if (!type_can_represent(type_get_base(lhs), type_get_base(rhs)))
2625 # The lhs type must be able to represent -at least- as
2626 # many elements as the RHS type
2627 if (get(lhs, p_count) < get(rhs, p_count))
2633 # A signed type could represent the full range of a smaller unsigned
2634 # type, but we don't bother; the two should agree when used in a SROM
2635 # layout. Instead simply assert that both are signed or unsigned.
2636 if (get(lhs, p_signed) != get(rhs, p_signed))
2639 # The `rhs` type must be equal or smaller in width to the `lhs` type
2640 if (get(lhs, p_width) < get(rhs, p_width))
2646 # Create a new ArrayType instance
2647 function array_type_new(type, count, _obj) {
2648 _obj = obj_new(ArrayType)
2649 set(_obj, p_type, type)
2650 set(_obj, p_count, count)
2656 # Parse a type string to either the Type, ArrayType, or null if
2657 # the type is not recognized.
2659 function parse_type_string(str, _base, _count) {
2660 if (match(str, ARRAY_REGEX"$") > 0) {
2661 # Extract count and base type
2662 _count = substr(str, RSTART+1, RLENGTH-2)
2663 sub(ARRAY_REGEX"$", "", str)
2665 # Look for base type
2666 if ((_base = type_named(str)) == null)
2669 return (array_type_new(_base, int(_count)))
2671 return (type_named(str))
2676 # Parse a variable name in the form of 'name' or 'name[len]', returning
2677 # either the provided base_type if no array specifiers are found, or
2678 # the fully parsed ArrayType.
2680 function parse_array_type_specifier(str, base_type, _count) {
2681 if (match(str, ARRAY_REGEX"$") > 0) {
2683 _count = substr(str, RSTART+1, RLENGTH-2)
2684 return (array_type_new(base_type, int(_count)))
2690 # Return the type constant for `name`, if any
2691 function type_named(name, _n, _type) {
2693 errorx("called type_named() with null name")
2695 if (map_contains(BaseTypes, name))
2696 return (map_get(BaseTypes, name))
2701 # Create a new Fmt instance
2702 function fmt_new(name, symbol, array_fmt, _obj) {
2704 set(_obj, p_name, name)
2705 set(_obj, p_symbol, symbol)
2707 if (array_fmt != null)
2708 set(_obj, p_array_fmt, array_fmt)
2714 # Return the Fmt constant for `name`, if any
2715 function fmt_named(name, _n, _fmt) {
2716 if (map_contains(ValueFormats, name))
2717 return (map_get(ValueFormats, name))
2722 # Create a new VFlag instance
2723 function vflag_new(name, constant, _obj) {
2724 _obj = obj_new(VFlag)
2725 set(_obj, p_name, name)
2726 set(_obj, p_const, constant)
2731 # Create a new StringConstant AST node
2732 function stringconstant_new(value, continued, _obj) {
2733 _obj = obj_new(StringConstant)
2734 set(_obj, p_value, value)
2735 set(_obj, p_continued, continued)
2736 set(_obj, p_line, NR)
2741 # Create an empty StringConstant AST node to which additional lines
2743 function stringconstant_empty(_obj) {
2744 return (stringconstant_new("", 1))
2747 # Parse an input string and return a new string constant
2749 function stringconstant_parse_line(line, _obj) {
2750 _obj = stringconstant_empty()
2751 stringconstant_append_line(_obj, line)
2755 # Parse and apend an additional line to this string constant
2756 function stringconstant_append_line(str, line, _cont, _strbuf, _regex, _eol) {
2757 obj_assert_class(str, StringConstant)
2759 # Must be marked for continuation
2760 if (!get(str, p_continued)) {
2761 errorx("can't append to non-continuation string '" \
2762 get(str, p_value) "'")
2765 _strbuf = get(str, p_value)
2767 # If start of string, look for (and remove) initial double quote
2768 if (_strbuf == null) {
2769 _regex = "^[ \t]*\""
2770 if (!sub(_regex, "", line)) {
2771 error("expected quoted string")
2775 # Look for a terminating double quote
2776 _regex = "([^\"\\\\]*(\\\\.[^\"\\\\]*)*)\""
2778 _eol = match(line, _regex)
2780 # Drop everything following the terminating quote
2781 line = substr(line, 1, RLENGTH-1)
2784 # No terminating quote found, continues on next line
2788 # Trim leading and trailing whitespace
2789 sub(/(^[ \t]+|[ \t]+$)/, "", line)
2791 # Append to existing buffer
2792 if ((_strbuf = get(str, p_value)) == NULL)
2793 set(str, p_value, line)
2795 set(str, p_value, _strbuf " " line)
2797 # Update line continuation setting
2798 set(str, p_continued, _cont)
2801 # Create a new RevRange instance
2802 function revrange_new(start, end, _obj) {
2803 _obj = obj_new(RevRange)
2804 set(_obj, p_start, start)
2805 set(_obj, p_end, end)
2806 set(_obj, p_line, NR)
2811 # Return true if the two revision ranges are equal
2812 function revrange_equal(lhs, rhs) {
2813 if (get(lhs, p_start) != get(rhs, p_start))
2816 if (get(lhs, p_end) != get(rhs, p_end))
2822 # Return true if the requested rev is covered by revrange, false otherwise
2823 function revrange_contains(range, rev) {
2824 obj_assert_class(range, RevRange)
2826 if (rev < get(range, p_start))
2828 else if (rev > get(range, p_end)) {
2836 # Return a string representation of the given revision range
2838 function revrange_to_string(revs, _start, _end) {
2839 obj_assert_class(revs, RevRange)
2841 _start = get(revs, p_start)
2842 _end = get(revs, p_end)
2846 else if (_end == REV_MAX)
2847 return (">= " _start)
2849 return (_start "-" _end)
2852 # Create a new VarGroup instance
2853 function var_group_new(name, _obj) {
2854 _obj = obj_new(VarGroup)
2855 set(_obj, p_name, name)
2856 set(_obj, p_vars, array_new())
2857 set(_obj, p_line, NR)
2862 # Create a new NVRAM instance
2863 function nvram_new(_obj, _vars, _v) {
2864 _obj = obj_new(NVRAM)
2866 set(_obj, p_vars, _vars)
2867 set(_obj, p_var_groups, array_new())
2868 set(_obj, p_srom_layouts, array_new())
2869 set(_obj, p_srom_table, map_new())
2872 # Register our implicit variable definitions
2875 # SROM signature offset
2876 _v = var_new(VAccessInternal, "<sromsig>", UInt16)
2877 array_append(_vars, _v)
2878 _g_var_names[get(_v, p_name)] = _v
2881 _v = var_new(VAccessInternal, "<sromcrc>", UInt8)
2882 array_append(_vars, _v)
2883 _g_var_names[get(_v, p_name)] = _v
2888 # Register a new SROM layout instance
2889 # An error will be thrown if the layout overlaps any revisions covered
2890 # by an existing instance.
2891 function nvram_add_srom_layout(nvram, layout, _table, _revs, _start, _end, _i) {
2892 obj_assert_class(nvram, NVRAM)
2893 obj_assert_class(layout, SromLayout)
2895 # revision:layout hash table
2896 _table = get(nvram, p_srom_table)
2898 # register the layout's revisions
2899 _revs = get(layout, p_revisions)
2900 _start = get(_revs, p_start)
2901 _end = get(_revs, p_end)
2903 for (_i = _start; _i <= _end; _i++) {
2904 if (map_contains(_table, _i)) {
2905 error("SROM layout redeclares layout for revision '" \
2906 _i "' (originally declared on line " \
2907 get(map_get(_table, _i), p_line) ")")
2910 map_set(_table, _i, layout)
2913 # append to srom_layouts
2914 array_append(get(nvram, p_srom_layouts), layout)
2917 # Return the first SROM layout registered for a given SROM revision,
2918 # or null if no matching layout is found
2919 function nvram_get_srom_layout(nvram, revision, _layouts, _nlayouts, _layout,
2922 obj_assert_class(nvram, NVRAM)
2924 _layouts = get(nvram, p_srom_layouts)
2925 _nlayouts = array_size(_layouts)
2926 for (_i = 0; _i < _nlayouts; _i++) {
2927 _layout = array_get(_layouts, _i)
2929 if (srom_layout_has_rev(_layout, revision))
2937 # Create a new Var instance
2938 function var_new(access, name, type, _obj) {
2939 obj_assert_class(access, VAccess)
2941 # Validate the variable identifier
2943 # The access modifier dictates the permitted identifier format.
2944 # VAccessInternal: <ident>
2945 # VAccess(Public|Private): ident
2946 if (access != VAccessInternal && name ~ SVAR_IDENT_REGEX) {
2947 error("invalid identifier '"name"'; did you mean to " \
2948 "mark this variable as internal?")
2949 } else if (access == VAccessInternal) {
2950 if (name !~ SVAR_IDENT_REGEX)
2951 error("invalid identifier '"name"' for internal " \
2952 "variable; did you mean '<" name ">'?")
2953 } else if (name !~ VAR_IDENT_REGEX) {
2954 error("invalid identifier '"name"'")
2958 set(_obj, p_access, access)
2959 set(_obj, p_name, name)
2960 set(_obj, p_type, type)
2961 set(_obj, p_line, NR)
2966 # Return true if var is internal-only, and should not be included
2967 # in any output (e.g. has an access specifier of VAccessInternal).
2968 function var_is_internal(var) {
2969 return (get(var, p_access) == VAccessInternal)
2972 # Return true if `var` has an array type
2973 function var_has_array_type(var, _vtype) {
2974 obj_assert_class(var, Var)
2975 _vtype = get(var, p_type)
2976 return (obj_is_instanceof(_vtype, ArrayType))
2979 # Return the number of array elements defined by this variable's type,
2980 # or 1 if the variable does not have an array type.
2981 function var_get_array_len(var) {
2982 obj_assert_class(var, Var)
2983 return (type_get_nelem(get(var, p_type)))
2986 # Return the fmt for var. If not explicitly set on var, will return then
2987 # return of calling type_get_default_fmt() with the variable's type
2988 function var_get_fmt(var, _fmt) {
2989 obj_assert_class(var, Var)
2991 # If defined on var, return it
2992 if ((_fmt = get(var, p_fmt)) != null)
2995 # Fall back on the type's default
2996 return (type_get_default_fmt(get(var, p_type)))
2999 # Return a new MacroDefine instance for the given variable, macro type,
3001 function var_get_macro(var, macro_type, value, _macro) {
3002 obj_assert_class(var, Var)
3003 obj_assert_class(macro_type, MacroType)
3005 return (macro_new(var_get_macro_name(var, macro_type), value))
3008 # Return the preprocessor constant name to be used with `var` for the given
3010 function var_get_macro_name(var, macro_type, _var_name, _suffix) {
3011 obj_assert_class(var, Var)
3012 obj_assert_class(macro_type, MacroType)
3014 _var_name = get(var, p_name)
3015 _suffix = get(macro_type, p_const_suffix)
3017 return("BHND_NVAR_" toupper(_var_name) _suffix)
3020 # Create a new SromLayout instance
3021 function srom_layout_new(rev_desc, _obj)
3023 _obj = obj_new(SromLayout)
3024 set(_obj, p_revisions, rev_desc)
3025 set(_obj, p_entries, array_new())
3026 set(_obj, p_revmap, map_new())
3027 set(_obj, p_output_var_counts, map_new())
3028 set(_obj, p_line, NR)
3033 # Register a new entry with the srom layout
3034 function srom_layout_add_entry(layout, entry, _revmap, _name, _rev_start,
3035 _rev_end, _var, _prev_entry, _count, _i)
3037 obj_assert_class(layout, SromLayout)
3038 obj_assert_class(entry, SromEntry)
3040 _layout_revmap = get(layout, p_revmap)
3041 _layout_var_count = get(layout, p_output_var_counts)
3043 _var = get(entry, p_var)
3044 _name = get(_var, p_name)
3046 # Add to revision array
3047 array_append(get(layout, p_entries), entry)
3049 # Add to the revision map tables
3050 _rev_start = get(get(entry, p_revisions), p_start)
3051 _rev_end = get(get(entry, p_revisions), p_end)
3053 for (_i = _rev_start; _i <= _rev_end; _i++) {
3054 # Check for existing entry
3055 _prev_entry = srom_layout_find_entry(layout, _name, _i)
3056 if (_prev_entry != null) {
3057 error("redefinition of variable '" _name "' for SROM " \
3058 "revision " _i " (previously defined on line " \
3059 get(_prev_entry, p_line) ")")
3062 # Add to the (varname,revision) map
3063 map_set(_layout_revmap, (_name SUBSEP _i), entry)
3065 # If an output variable, set or increment the output variable
3067 if (!srom_entry_should_output(entry, _i))
3070 if (!map_contains(_layout_var_count, _i)) {
3071 map_set(_layout_var_count, _i, 1)
3073 _count = map_get(_layout_var_count, _i)
3074 map_set(_layout_var_count, _i, _count + 1)
3080 # Return the variable name to be used when emitting C declarations
3081 # for this SROM layout
3083 # The name is gauranteed to be unique across SROM layouts with non-overlapping
3085 function srom_layout_get_variable_name(layout, _revs) {
3086 obj_assert_class(layout, SromLayout)
3088 _revs = get(layout, p_revisions)
3090 return ("bhnd_sprom_layout_r" get(_revs, p_start) \
3091 "_r" get(_revs, p_end))
3094 # Return true if the given SROM revision is defined by the layout, false
3096 function srom_layout_has_rev(layout, rev) {
3097 obj_assert_class(layout, SromLayout)
3098 return (revrange_contains(get(layout, p_revisions), rev))
3102 # Return the total number of output variables (variables to be included
3103 # in the SROM layout bindings) for the given SROM revision
3104 function srom_layout_num_output_vars(layout, rev, _counts)
3106 obj_assert_class(layout, SromLayout)
3108 _counts = get(layout, p_output_var_counts)
3109 if (!map_contains(_counts, rev))
3112 return (map_get(_counts, rev))
3115 # Return the SromEntry defined for the given variable name and SROM revision,
3117 function srom_layout_find_entry(layout, vname, revision, _key, _srom_revmap) {
3118 obj_assert_class(layout, SromLayout)
3120 _srom_revmap = get(layout, p_revmap)
3122 # SromEntry are mapped by name,revision composite keys
3123 _key = vname SUBSEP revision
3124 if (!map_contains(_srom_revmap, _key))
3127 return (map_get(_srom_revmap, _key))
3131 # Create a new SromLayoutFilter instance, checking that `revs`
3132 # falls within the parent's revision range
3133 function srom_layout_filter_new(parent, revs, _obj, _start, _end, _parent_revs) {
3134 obj_assert_class(parent, SromLayout)
3135 obj_assert_class(revs, RevRange)
3137 # Fetch our parent's revision range, confirm that we're
3139 _start = get(revs, p_start)
3140 _end = get(revs, p_end)
3141 _parent_revs = get(parent, p_revisions)
3143 if (!revrange_contains(_parent_revs, _start))
3144 error("'" _start "' is outside of parent range")
3146 if (!revrange_contains(_parent_revs, _end))
3147 error("'" _end "' is outside of parent range")
3149 if (revrange_equal(revs, _parent_revs)) {
3150 error("srom range '" revrange_to_string(revs) "' is " \
3151 "identical to parent range of '" \
3152 revrange_to_string(_parent_revs) "'")
3155 # Construct and return new filter instance
3156 _obj = obj_new(SromLayoutFilter)
3157 set(_obj, p_parent, parent)
3158 set(_obj, p_revisions, revs)
3159 set(_obj, p_line, NR)
3165 # Create a new SromEntry instance
3167 # var: The variable referenced by this entry
3168 # revisions: The SROM revisions to which this entry applies
3169 # base_offset: The SROM entry offset; any relative segment offsets will be
3170 # calculated relative to the base offset
3171 # type: The SROM's value type; this may be a subtype of the variable
3172 # type, and defines the data (width, sign, etc) to be read from
3175 function srom_entry_new(var, revisions, base_offset, type, _obj) {
3176 obj_assert_class(var, Var)
3177 if (revisions != null)
3178 obj_assert_class(revisions, RevRange)
3180 _obj = obj_new(SromEntry)
3181 set(_obj, p_var, var)
3182 set(_obj, p_revisions, revisions)
3183 set(_obj, p_base_offset, base_offset)
3184 set(_obj, p_type, type)
3185 set(_obj, p_offsets, array_new())
3186 set(_obj, p_line, NR)
3191 # Return true if the SromEntry has an array type
3192 function srom_entry_has_array_type(entry) {
3193 obj_assert_class(entry, SromEntry)
3195 return (obj_is_instanceof(get(entry, p_type), ArrayType))
3198 # Return the number of array elements defined by this SromEntry's type,
3199 # or 1 if the entry does not have an array type.
3200 function srom_entry_get_array_len(entry, _type) {
3201 obj_assert_class(entry, SromEntry)
3203 return (type_get_nelem(get(entry, p_type)))
3207 # Return true if the given entry should be included in the output bindings
3208 # generated for the given revision, false otherwise.
3210 function srom_entry_should_output(entry, rev, _var, _revs)
3212 obj_assert_class(entry, SromEntry)
3214 _var = get(entry, p_var)
3215 _revs = get(entry, p_revisions)
3217 # Exclude internal variables
3218 if (var_is_internal(_var))
3221 # Exclude inapplicable entry revisions
3222 if (!revrange_contains(_revs, rev))
3229 # Return the single, non-shifted, non-masked offset/segment for the given
3230 # SromEntry, or throw an error if the entry contains multiple offsets/segments.
3232 # This is used to fetch special-cased variable definitions that are required
3233 # to present a single simple offset.
3235 function srom_entry_get_single_segment(entry, _offsets, _segments, _seg,
3236 _base_type, _default_mask)
3238 obj_assert_class(entry, SromEntry)
3240 # Fetch the single offset's segment list
3241 _offsets = get(entry, p_offsets)
3242 if (array_size(_offsets) != 1)
3243 errorc(get(entry, p_line), "unsupported offset count")
3245 _segments = get(array_first(_offsets), p_segments)
3246 if (array_size(_segments) != 1)
3247 errorc(get(entry, p_line), "unsupported segment count")
3249 # Fetch the single segment
3250 _seg = array_first(_segments)
3251 _base_type = srom_segment_get_base_type(_seg)
3252 _default_mask = get(_base_type, p_mask)
3254 # Must not be shifted/masked
3255 if (get(_seg, p_shift) != 0)
3256 errorc(obj_get_prop_nr(_seg, p_mask), "shift unsupported")
3258 if (get(_seg, p_mask) != _default_mask)
3259 errorc(obj_get_prop_nr(_seg, p_mask), "mask unsupported")
3264 # Create a new SromOffset instance
3265 function srom_offset_new(_obj) {
3266 _obj = obj_new(SromOffset)
3267 set(_obj, p_segments, array_new())
3268 set(_obj, p_line, NR)
3273 # Return the number of SromSegment instances defined by this offset.
3274 function srom_offset_segment_count(offset) {
3275 obj_assert_class(offset, SromOffset)
3276 return (array_size(get(offset, p_segments)))
3279 # Return the idx'th segment. Will throw an error if idx falls outside
3280 # the number of available segments.
3281 function srom_offset_get_segment(offset, idx, _segments, _seg) {
3282 obj_assert_class(offset, SromOffset)
3284 return (array_get(get(offset, p_segments), idx))
3287 # Create a new SromSegment instance
3288 function srom_segment_new(offset, type, mask, shift, value, _obj) {
3289 _obj = obj_new(SromSegment)
3290 set(_obj, p_offset, offset)
3291 set(_obj, p_type, type)
3292 set(_obj, p_mask, mask)
3293 set(_obj, p_shift, shift)
3294 set(_obj, p_value, value)
3295 set(_obj, p_line, NR)
3300 # Return true if the segment has an array type
3301 function srom_segment_has_array_type(seg, _type) {
3302 _type = srom_segment_get_type(seg)
3303 return (obj_is_instanceof(_type, ArrayType))
3306 # Return the array count of the segment, or 1 if the segment does not have
3308 function srom_segment_get_array_len(seg, _type) {
3309 if (!srom_segment_has_array_type(seg))
3312 _type = srom_segment_get_type(seg)
3313 return (get(_type, p_count))
3316 # Return the type of the segment
3317 function srom_segment_get_type(seg) {
3318 obj_assert_class(seg, SromSegment)
3319 return (get(seg, p_type))
3323 # Return the base type of the segment
3324 function srom_segment_get_base_type(seg) {
3325 return (type_get_base(srom_segment_get_type(seg)))
3328 # Return true if the two segments have identical types and attributes (i.e.
3329 # differing only by offset)
3330 function srom_segment_attributes_equal(lhs, rhs) {
3331 obj_assert_class(lhs, SromSegment)
3332 obj_assert_class(rhs, SromSegment)
3335 if (!type_equal(get(lhs, p_type), get(rhs, p_type)))
3339 if (get(lhs, p_mask) != get(rhs, p_mask))
3343 if (get(lhs, p_shift) != get(rhs, p_shift))
3347 if (get(lhs, p_value) != get(rhs, p_value))
3353 # Return a human-readable representation of a Segment instance
3354 function segment_to_string(seg, _str, _t, _m, _s, _attrs, _attr_str) {
3355 _attrs = array_new()
3357 # include type (if specified)
3358 if ((_t = get(seg, p_type)) != null)
3359 _str = (type_to_string(_t) " ")
3362 _str = (_str sprintf("0x%X", get(seg, p_offset)))
3364 # append list of attributes
3365 if ((_m = get(seg, p_mask)) != null)
3366 array_append(_attrs, ("&" _m))
3368 if ((_s = get(seg, p_shift)) != null) {
3373 array_append(_attrs, _s)
3376 _attr_str = array_join(_attrs, ", ")
3379 if (_attr_str == "")
3382 return (_str " (" _attr_str ")")
3385 # return the flag definition for variable `v`
3386 function gen_var_flags(v, _type, _flags, _flag, _str)
3389 _type = get(v, p_type)
3390 _flags = array_new()
3393 if (get(v, p_access) == VAccessPrivate)
3394 array_append(_flags, VFlagPrivate)
3397 if (get(v, p_ignall1))
3398 array_append(_flags, VFlagIgnoreAll1)
3400 # If empty, return empty flag value
3401 if (array_size(_flags) == 0) {
3406 # Join all flag constants with |
3407 _str = array_join(_flags, "|", class_get_prop_id(VFlag, p_const))
3416 # Return the absolute value
3419 return (i < 0 ? -i : i)
3423 # Return the minimum of two values
3425 function min(lhs, rhs) {
3426 return (lhs < rhs ? lhs : rhs)
3430 # Return the maximum of two values
3432 function max(lhs, rhs) {
3433 return (lhs > rhs ? lhs : rhs)
3437 # Parse a hex string
3439 function parse_hex_string(str, _hex_pstate, _out, _p, _count) {
3440 if (!AWK_REQ_HEX_PARSING)
3443 # Populate hex parsing lookup table on-demand
3444 if (!("F" in _g_hex_table)) {
3445 for (_p = 0; _p < 16; _p++) {
3446 _g_hex_table[sprintf("%X", _p)] = _p
3447 _g_hex_table[sprintf("%x", _p)] = _p
3451 # Split input into an array
3452 _count = split(toupper(str), _hex_pstate, "")
3456 if (_count >= 2 && _hex_pstate[1] == "0") {
3457 if (_hex_pstate[2] == "x" || _hex_pstate[2] == "X")
3461 # Parse the hex_digits
3463 for (; _p <= _count; _p++)
3464 _out = (_out * 16) + _g_hex_table[_hex_pstate[_p]]
3470 # Return the integer representation of an unsigned decimal, hexadecimal, or
3473 function parse_uint_string(str) {
3474 if (str ~ UINT_REGEX)
3476 else if (str ~ HEX_REGEX)
3477 return (parse_hex_string(str))
3479 error("invalid integer value: '" str "'")
3483 # Parse an offset string, stripping any leading '+' or trailing ':' or ','
3490 function parse_uint_offset(str) {
3491 # Drop any leading '+'
3494 # Drop any trailing ':', ',', or '|'
3495 sub("[,|:]$", "", str)
3497 # Parse the cleaned up string
3498 return (parse_uint_string(str))
3502 # Print msg to output file, without indentation
3504 function emit_ni(msg) {
3505 printf("%s", msg) >> OUTPUT_FILE
3509 # Print msg to output file, indented for the current `output_depth`
3511 function emit(msg, _ind) {
3512 for (_ind = 0; _ind < output_depth; _ind++)
3519 # Print a warning to stderr
3521 function warn(msg) {
3522 print "warning:", msg, "at", FILENAME, "line", NR > "/dev/stderr"
3526 # Print an warning message without including the source line information
3528 function warnx(msg) {
3529 print "warning:", msg > "/dev/stderr"
3533 # Print a compiler error to stderr with a caller supplied
3536 function errorc(line, msg) {
3537 errorx(msg " at " FILENAME " line " line)
3541 # Print a compiler error to stderr
3543 function error(msg) {
3544 errorx(msg " at " FILENAME " line " NR ":\n\t" $0)
3548 # Print an error message without including the source line information
3550 function errorx(msg) {
3551 print "error:", msg > "/dev/stderr"
3557 # Print a debug output message
3559 function debug(msg, _i) {
3562 for (_i = 1; _i < _g_parse_stack_depth; _i++)
3563 printf("\t") > "/dev/stderr"
3564 print msg > "/dev/stderr"
3568 # Advance to the next non-comment input record
3570 function next_line(_result) {
3573 } while (_result > 0 && $0 ~ /^[ \t]*#.*/) # skip comment lines
3578 # Advance to the next input record and verify that it matches @p regex
3580 function getline_matching(regex, _result) {
3581 _result = next_line()
3592 # Shift the current fields left by `n`.
3594 # If all fields are consumed and the optional do_getline argument is true,
3595 # read the next line.
3597 function shiftf(n, do_getline, _i) {
3599 error("shift past end of line")
3602 # If shifting the entire line, just reset the line value
3605 for (_i = 1; _i <= NF-n; _i++) {
3611 if (NF == 0 && do_getline)
3615 # Push a new parser state.
3616 function parser_state_push(ctx, is_block, _state) {
3617 _state = obj_new(ParseState)
3618 set(_state, p_ctx, ctx)
3619 set(_state, p_is_block, is_block)
3620 set(_state, p_line, NR)
3622 _g_parse_stack_depth++
3623 _g_parse_stack[_g_parse_stack_depth] = _state
3626 # Fetch the current parser state
3627 function parser_state_get() {
3628 if (_g_parse_stack_depth == 0)
3629 errorx("parser_state_get() called with empty parse stack")
3631 return (_g_parse_stack[_g_parse_stack_depth])
3634 # Pop the current parser state
3635 function parser_state_pop(_block_state, _closes_block) {
3636 if (_g_parse_stack_depth == 0)
3637 errorx("parser_state_pop() called with empty parse stack")
3639 _closes_block = get(parser_state_get(), p_is_block)
3641 delete _g_parse_stack[_g_parse_stack_depth]
3642 _g_parse_stack_depth--
3648 # Fetch the current context object associated with this parser state
3649 # The object will be asserted as being an instance of the given class.
3650 function parser_state_get_context(class, _ctx_obj) {
3651 _ctx_obj = get(parser_state_get(), p_ctx)
3652 obj_assert_class(_ctx_obj, class)
3657 # Walk the parser state stack until a context object of the given class
3658 # is found. If the top of the stack is reached without finding a context object
3659 # of the requested type, an error will be thrown.
3660 function parser_state_find_context(class, _state, _ctx, _i) {
3662 errorx("parser_state_find_context() called with null class")
3664 # Find the first context instance inheriting from `class`
3665 for (_i = 0; _i < _g_parse_stack_depth; _i++) {
3666 _state = _g_parse_stack[_g_parse_stack_depth - _i]
3667 _ctx = get(_state, p_ctx)
3670 if (obj_is_instanceof(_ctx, class))
3675 errorx("no context instance of type '" class_get_name(class) "' " \
3676 "found in parse stack")
3680 # Find opening brace and push a new parser state for a brace-delimited block.
3682 function parser_state_open_block(ctx) {
3683 if ($0 ~ "{" || getline_matching("^[ \t]*{") > 0) {
3684 parser_state_push(ctx, 1)
3685 sub("^[^{]*{", "", $0)
3689 error("found '"$1 "' instead of expected '{'")
3693 # Find closing brace and pop parser states until the first
3694 # brace-delimited block is discarded.
3696 function parser_state_close_block(_next_state, _found_block) {
3698 error("internal error - no closing brace")
3700 # pop states until we exit the first enclosing block
3702 _next_state = parser_state_get()
3703 _found_block = get(_next_state, p_is_block)
3705 } while (!_found_block)
3707 # strip everything prior to the block closure
3708 sub("^[^}]*}", "", $0)
3711 # Evaluates to true if the current parser state is defined with a context of
3713 function in_parser_context(class, _ctx) {
3715 errorx("called in_parser_context() with null class")
3717 _ctx = get(parser_state_get(), p_ctx)
3718 return (obj_is_instanceof(_ctx, class))
3722 # Parse and return a revision range from the current line.
3725 # 4-10 # revisions 4-10, inclusive
3731 function parse_revrange(_start, _end, _robj) {
3735 if ($2 ~ "[0-9]*-[0-9*]") {
3736 split($2, _g_rev_range, "[ \t]*-[ \t]*")
3737 _start = int(_g_rev_range[1])
3738 _end = int(_g_rev_range[2])
3739 } else if ($2 ~ "(>|>=|<|<=)" && $3 ~ "[1-9][0-9]*") {
3743 } else if ($2 == ">=") {
3746 } else if ($2 == "<" && int($3) > 0) {
3749 } else if ($2 == "<=") {
3753 error("invalid revision descriptor")
3755 } else if ($2 ~ "[1-9][0-9]*") {
3759 error("invalid revision descriptor")
3762 return (revrange_new(_start, _end))
3766 # Parse a variable group block starting at the current line
3768 # group "Group Name" {
3775 function parse_variable_group(_ctx, _groups, _group, _group_name) {
3776 _ctx = parser_state_get_context(NVRAM)
3778 # Seek to the start of the name string
3781 # Parse the first line
3782 _group_name = stringconstant_parse_line($0)
3784 # Incrementally parse line continuations
3785 while (get(_group_name, p_continued)) {
3787 stringconstant_append_line(_group_name, $0)
3790 debug("group \"" get(_group_name, p_value) "\" {")
3792 # Register the new variable group
3793 _groups = get(_ctx, p_var_groups)
3794 _group = var_group_new(_group_name)
3795 array_append(_groups, _group)
3797 # Push our variable group block
3798 parser_state_open_block(_group)
3803 # Parse a variable definition block starting at the current line
3810 function parse_variable_defn(_ctx, _vaccess, _type, _name, _fmt, _var,
3813 _ctx = parser_state_get_context(SymbolContext)
3815 # Check for access modifier
3816 if ($1 == "private") {
3817 _vaccess = VAccessPrivate
3819 } else if ($1 == "internal") {
3820 _vaccess = VAccessInternal
3823 _vaccess = VAccessPublic
3826 # Find the base type
3827 if ((_type = type_named($1)) == null)
3828 error("unknown type '" $1 "'")
3830 # Parse (and trim) any array specifier from the variable name
3832 _type = parse_array_type_specifier(_name, _type)
3833 sub(ARRAY_REGEX"$", "", _name)
3835 # Look for an existing variable definition
3836 if (_name in _g_var_names) {
3837 error("variable identifier '" _name "' previously defined at " \
3838 "line " get(_g_var_names[_name], p_line))
3841 # Construct new variable instance
3842 _var = var_new(_vaccess, _name, _type)
3843 debug((_private ? "private " : "") type_to_string(_type) " " _name " {")
3845 # Register in global name table
3846 _g_var_names[_name] = _var
3848 # Add to our parent context
3849 _var_list = get(_ctx, p_vars)
3850 array_append(_var_list, _var)
3852 # Push our variable definition block
3853 parser_state_open_block(_var)
3858 # Return a string containing the human-readable list of valid Fmt names
3860 function fmt_get_human_readable_list(_result, _fmts, _fmt, _nfmts, _i)
3862 # Build up a string listing the valid formats
3863 _fmts = map_to_array(ValueFormats)
3866 _nfmts = array_size(_fmts)
3867 for (_i = 0; _i < _nfmts; _i++) {
3868 _fmt = array_get(_fmts, _i)
3870 _result = _result "or "
3872 _result = _name_str \
3873 "'" get(_fmt, p_name) "'"
3876 _result = _result ", "
3884 # Parse a variable parameter from the current line
3886 # fmt (decimal|hex|macaddr|...)
3888 # desc "quoted string"
3889 # help "quoted string"
3891 function parse_variable_param(param_name, _var, _vprops, _prop_id, _pval) {
3892 _var = parser_state_get_context(Var)
3894 if (param_name == "fmt") {
3897 # Check for an existing definition
3898 if ((_pval = get(_var, p_fmt)) != null) {
3899 error("fmt previously specified on line " \
3900 obj_get_prop_nr(_var, p_fmt))
3903 # Validate arguments
3905 error("'" $1 "' requires a single parameter value of " \
3906 fmt_get_human_readable_list())
3909 if ((_pval = fmt_named($2)) == null) {
3910 error("'" $1 "' value '" $2 "' unrecognized. Must be " \
3911 "one of " fmt_get_human_readable_list())
3915 set(_var, p_fmt, _pval)
3916 } else if (param_name == "all1") {
3919 # Check for an existing definition
3920 if ((_pval = get(_var, p_ignall1)) != null) {
3921 error("all1 previously specified on line " \
3922 obj_get_prop_nr(_var, p_ignall1))
3927 error("'" $1 "'requires a single 'ignore' argument")
3928 else if ($2 != "ignore")
3929 error("unknown "$1" value '"$2"', expected 'ignore'")
3931 # Set variable property
3932 set(_var, p_ignall1, 1)
3933 } else if (param_name == "desc" || param_name == "help") {
3934 # Fetch an indirect property reference for either the 'desc'
3935 # or 'help' property
3936 _prop_id = obj_get_named_prop_id(_var, param_name)
3938 # Check for an existing definition
3939 if ((_pval = prop_get(_var, _prop_id)) != null) {
3940 error(get(_var, p_name) " '" $1 "' redefined " \
3941 "(previously defined on line " \
3942 obj_get_prop_id_nr(_var, _prop_id) ")")
3945 # Seek to the start of the desc/help string
3948 # Parse the first line
3949 _pval = stringconstant_parse_line($0)
3951 # Incrementally parse line continuations
3952 while (get(_pval, p_continued)) {
3954 stringconstant_append_line(_pval, $0)
3957 debug(param_name " \"" get(_pval, p_value) "\"")
3959 # Add to the var object
3960 prop_set(_var, _prop_id, _pval)
3962 error("unknown variable property type: '" param_name "'")
3968 # Parse a top-level SROM layout block starting at the current line
3974 function parse_srom_layout(_nvram, _srom_layouts, _revs, _layout) {
3975 _nvram = parser_state_get_context(NVRAM)
3976 _srom_layouts = get(_nvram, p_srom_layouts)
3978 # Parse revision descriptor and register SROM
3980 _revs = parse_revrange()
3981 _layout = srom_layout_new(_revs)
3982 nvram_add_srom_layout(_nvram, _layout)
3984 debug("srom " revrange_to_string(_revs) " {")
3986 # Push new SROM parser state
3987 parser_state_open_block(_layout)
3992 # Parse a nested srom range filter block starting at the current line
4000 function parse_srom_layout_filter(_parent, _revs, _filter) {
4001 _parent = parser_state_get_context(SromLayout)
4003 # Parse revision descriptor
4004 _revs = parse_revrange()
4006 # Construct the filter (which also validates the revision range)
4007 _filter = srom_layout_filter_new(_parent, _revs)
4009 debug("srom " revrange_to_string(_revs) " {")
4011 # Push new SROM parser state
4012 parser_state_open_block(_filter)
4017 # Parse a SROM offset segment's attribute list from the current line
4020 # (&0xF0, >>4, =0x5340)
4023 # Attribute designators:
4024 # &0xF Mask value with 0xF
4025 # <<4 Shift left 4 bits
4026 # >>4 Shift right 4 bits
4027 # =0x53 The parsed value must be equal to this constant value
4029 # May be followed by a | indicating that this segment should be OR'd with the
4030 # segment that follows, or a terminating , indicating that a new offset's
4031 # list of segments may follow.
4033 function parse_srom_segment_attributes(offset, type, _attrs, _num_attr, _attr,
4034 _mask, _shift, _value, _i)
4036 # seek to offset (attributes...) or end of the offset expr (|,)
4037 sub("^[^,(|){}]+", "", $0)
4040 _mask = type_get_default_mask(type)
4045 # extract attribute list
4046 if (match($0, /\([^|\(\)]*\)/) <= 0)
4047 error("expected attribute list")
4049 _attrs = substr($0, RSTART+1, RLENGTH-2)
4051 # drop attribute list from the input line
4052 $0 = substr($0, RSTART+RLENGTH, length($0) - RSTART+RLENGTH)
4055 _num_attr = split(_attrs, _g_attrs, ",[ \t]*")
4056 for (_i = 1; _i <= _num_attr; _i++) {
4057 _attr = _g_attrs[_i]
4059 if (sub("^&[ \t]*", "", _attr) > 0) {
4060 _mask = parse_uint_string(_attr)
4061 } else if (sub("^<<[ \t]*", "", _attr) > 0) {
4062 _shift = - parse_uint_string(_attr)
4063 } else if (sub("^>>[ \t]*", "", _attr) > 0) {
4064 _shift = parse_uint_string(_attr)
4065 } else if (sub("^=[ \t]*", "", _attr) > 0) {
4068 error("unknown attribute '" _attr "'")
4073 return (srom_segment_new(offset, type, _mask, _shift, _value))
4077 # Parse a SROM offset's segment declaration from the current line
4079 # +0x0: u8 (&0xF0, >>4) # read 8 bits at +0x0 (relative to srom entry
4080 # # offset, apply 0xF0 mask, shift >> 4
4081 # 0x10: u8 (&0xF0, >>4) # identical to above, but perform the read at
4082 # # absolute offset 0x10
4084 # +0x0: u8 # no attributes
4087 # +0x0 # simplified forms denoted by lack of ':'; the
4088 # 0x0 # type is inherited from the parent SromEntry
4091 function parse_srom_segment(base_offset, base_type, _simple, _type, _type_str,
4092 _offset, _attrs, _num_attr, _attr, _mask, _shift, _off_desc)
4094 # Fetch the offset value
4097 # Offset string must be one of:
4098 # simplified entry: <offset|+reloff>
4099 # Provides only the offset, with the type inherited
4100 # from the original variable definition
4101 # standard entry: <offset|+reloff>:
4102 # Provides the offset, followed by a type
4104 # We differentiate the two by looking for (and simultaneously removing)
4106 if (!sub(/:$/, "", _offset))
4109 # The offset may either be absolute (e.g. 0x180) or relative (e.g.
4112 # If we find a relative offset definition, we must trim the leading '+'
4113 # and then add the base offset
4114 if (sub(/^\+/, "", _offset)) {
4115 _offset = base_offset + parse_uint_offset(_offset)
4118 _offset = parse_uint_offset(_offset)
4121 # If simplified form, use the base type of the SROM entry. Otherwise,
4122 # we need to parse the type.
4127 sub(/,$/, "", _type_str) # trim trailing ',', if any
4129 if ((_type = parse_type_string(_type_str)) == null)
4130 error("unknown type '" _type_str "'")
4133 # Parse the trailing (... attributes ...), if any
4134 return (parse_srom_segment_attributes(_offset, _type))
4138 # Parse a SROM variable entry from the current line
4139 # <offset>: <type> <varname><array spec> ...
4141 function parse_srom_variable_entry(_srom, _srom_revs, _rev_start, _rev_end,
4142 _srom_entries, _srom_revmap, _prev_entry, _ctx, _base_offset, _name,
4143 _stype, _var, _entry, _offset, _seg, _i)
4145 # Fetch our parent context
4146 _ctx = parser_state_get_context(SromContext)
4147 _srom_revs = get(_ctx, p_revisions)
4148 _rev_start = get(_srom_revs, p_start)
4149 _rev_end = get(_srom_revs, p_end)
4151 # Locate our enclosing layout
4152 _srom = parser_state_find_context(SromLayout)
4153 _srom_entries = get(_srom, p_entries)
4154 _srom_revmap = get(_srom, p_revmap)
4156 # Verify argument count
4158 error("unrecognized srom entry syntax; must specify at " \
4159 "least \"<offset>: <type> <variable name>\"")
4162 # Parse the base offset
4163 _base_offset = parse_uint_offset($1)
4165 # Parse the base type
4166 if ((_stype = type_named($2)) == null)
4167 error("unknown type '" $2 "'")
4169 # Parse (and trim) any array specifier from the variable name
4171 _stype = parse_array_type_specifier(_name, _stype)
4172 sub(ARRAY_REGEX"$", "", _name)
4174 # Locate the variable definition
4175 if (!(_name in _g_var_names))
4176 error("no definition found for variable '" _name "'")
4177 _var = _g_var_names[_name]
4179 # The SROM entry type must be a subtype of the variable's declared
4181 if (!type_can_represent(get(_var, p_type), _stype)) {
4182 error("'" type_to_string(_stype) "' SROM value cannot be " \
4183 "coerced to '" type_to_string(get(_var, p_type)) " " _name \
4187 # Create and register our new offset entry
4188 _entry = srom_entry_new(_var, _srom_revs, _base_offset, _stype)
4189 srom_layout_add_entry(_srom, _entry)
4191 # Seek to either the block start ('{'), or the attributes to be
4192 # used for a single offset/segment entry at `offset`
4195 # Using the block syntax? */
4197 debug(sprintf("0x%03x: %s %s {", _base_offset,
4198 type_to_string(_stype), _name))
4199 parser_state_open_block(_entry)
4201 # Otherwise, we're using the simplified syntax -- create and
4202 # register our implicit SromOffset
4203 _offset = srom_offset_new()
4204 array_append(get(_entry, p_offsets), _offset)
4206 # Parse and register simplified segment syntax
4207 _seg = parse_srom_segment_attributes(_base_offset, _stype)
4208 array_append(get(_offset, p_segments), _seg)
4210 debug(sprintf("0x%03x: %s %s { %s }", _base_offset,
4211 type_to_string(_stype), _name, segment_to_string(_seg)))
4216 # Parse all SromSegment entry segments readable starting at the current line
4218 # <offset|+reloff>[,|]?
4219 # <offset|+reloff>: <type>[,|]?
4220 # <offset|+reloff>: <type> (<attributes>)[,|]?
4222 function parse_srom_entry_segments(_entry, _base_off, _base_type, _offs,
4223 _offset, _segs, _seg, _more_seg, _more_vals)
4225 _entry = parser_state_get_context(SromEntry)
4226 _base_off = get(_entry, p_base_offset)
4227 _offs = get(_entry, p_offsets)
4229 _base_type = get(_entry, p_type)
4230 _base_type = type_get_base(_base_type)
4234 # Create a SromOffset
4235 _offset = srom_offset_new()
4236 _segs = get(_offset, p_segments)
4238 array_append(_offs, _offset)
4240 # Parse all segments
4242 _seg = parse_srom_segment(_base_off, _base_type)
4243 array_append(_segs, _seg)
4245 # Do more segments follow?
4246 _more_seg = ($1 == "|")
4251 debug(segment_to_string(_seg) " |")
4253 debug(segment_to_string(_seg))
4256 # Do more offsets follow?
4257 _more_vals = ($1 == ",")
4260 } while (_more_vals)