4 * genmmio I/O register definition parser.
6 * Copyright (c) 2010 Nathan Keynes.
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
28 #include <glib/gmem.h>
34 * start: start register_block | ;
35 * register_block: 'registers' IDENTIFIER 'at' INTEGER opt_paramlist '{' register_list '}';
36 * opt_paramlist: '(' param_list ')' | '(' ')' | ;
37 * param_list: param_list ',' param | param ;
38 * param: access_param | fill_param | endian_param | mask_param ;
39 * access_param: 'access' '=' 'any' // Behaves as if it were memory
40 * | 'access' '=' 'nooffset' // Recognize any size access (zero-ext or trunc) as long as the address is exactly the same
41 * | 'access' '=' 'exact' // Recognize only the exact access type at the exact address
43 * fill_param: 'fill' '=' literal;
44 * endian_param: 'endian' '=' 'little'
45 * | 'endian' '=' 'big'
47 * mask_param: 'mask' '=' literal;
49 * register_list: register_list register_desc | ;
50 * register_desc: INTEGER ':' mode_spec type_spec IDENTIFIER opt_paramlist [ '=' literal ] [ ACTION ]
51 * mode_spec: 'const' | 'rw' | 'ro' | 'wo';
52 * type_spec: 'i8' | 'i16' | 'i32' | 'i64' | 'f32' | 'f64' | 'string' | 'cstring';
53 * literal: INTEGER | STRING | 'undefined' ;
55 * C-style comments are recognized as such.
58 #define TOK_IDENTIFIER 1
73 #define TOK_LSQUARE 16
74 #define TOK_RSQUARE 17
80 #define TOK_INCLUDE 23
82 #define TOK_REGISTERS 25
87 #define TOK_UNDEFINED 30
90 #define FIRST_KEYWORD TOK_ACCESS
91 #define LAST_KEYWORD TOK_UNDEFINED
92 static const char *TOKEN_NAMES[] = {"NULL", "IDENTIFIER", "INTEGER", "STRING", "ACTION", "EOF", "ERROR",
93 "(", ")", "{", "}", ",", ":", ";", ".", "..", "[", "]", "=",
94 "ACCESS", "AT", "ENDIAN", "FILL", "INCLUDE", "MASK", "REGISTERS", "SPACE", "STRIDE", "TEST", "TRACE", "UNDEFINED" };
95 static const char *MODE_NAMES[] = {"const", "rw", "ro", "wo", "mirror"};
96 static const char *TYPE_NAMES[] = {"i8", "i16", "i32", "i64", "f32", "f64", "string"};
97 static int TYPE_SIZES[] = {1,2,4,8,4,8,0};
98 static const char *ENDIAN_NAMES[] = {"default", "little", "big"};
99 static const char *ACCESS_NAMES[] = {"default", "any", "nooffset", "exact"};
100 static const char *TRACE_NAMES[] = {"default", "off", "on"};
102 #define elementsof(x) (sizeof(x)/sizeof(x[0]))
104 typedef struct token_data {
116 static char *yybuffer;
117 static char *yyposn, *yylineposn;
120 static struct token_data yytok;
122 #define YYPARSE_ERROR( msg, ... ) \
124 fprintf( stderr, "Parse error at %d:%d: " msg "\n", yytok.yyline, yytok.yycol, __VA_ARGS__ ); \
129 { int _tok = iolex(x); \
130 if( _tok != (x) ) { \
131 YYPARSE_ERROR( "Expected %s but got %s", TOKEN_NAMES[x], TOKEN_NAMES[_tok] ); \
135 static int iolex( int expectToken );
136 static int iolex_open( const char *filename );
137 static void iolex_close();
139 static inline char *yystrdup()
141 char *str = g_malloc0(yytok.yylength+1);
142 memcpy( str, yytok.yytext, yytok.yylength);
146 static inline int yystrcasecmp(const char *cmp)
148 int len = strlen(cmp);
149 if( len != yytok.yylength ) {
150 return yytok.yylength - len;
152 return strncasecmp(yytok.yytext, cmp, yytok.yylength);
155 static int yymatch( const char *arr[], unsigned numOptions )
157 for( unsigned i=0; i<numOptions; i++ ) {
158 if( yystrcasecmp( arr[i] ) == 0 ) {
165 static gint register_block_sort_cb( gconstpointer a, gconstpointer b )
167 struct regblock *blocka = (struct regblock *)a;
168 struct regblock *blockb = (struct regblock *)b;
169 return blocka->address - blockb->address;
172 static int register_ptr_sort_cb( const void *a, const void *b )
174 regdef_t *ptra = (regdef_t *)a;
175 regdef_t *ptrb = (regdef_t *)b;
176 if( (*ptra)->offset != (*ptrb)->offset )
177 return (*ptra)->offset - (*ptrb)->offset;
178 return (*ptra)->type - (*ptrb)->type;
181 static void ioparse_apval(int tok, union apval *apv, unsigned *numBytes)
183 if( tok == TOK_INTEGER ) {
185 if( *numBytes == 0 ) {
186 YYPARSE_ERROR( "Expected string initializer but was an integer (0x%x)", yytok.v.i );
188 } else if( tok = TOK_STRING ) {
189 if( *numBytes == 0 ) {
190 *numBytes = yytok.slen;
193 if( *numBytes != yytok.slen ) {
194 YYPARSE_ERROR( "Expected %d byte initializer but was %d", *numBytes, yytok.slen );
196 assert( *numBytes < sizeof(uint64_t) );
198 /* FIXME: handle endian mismatches */
199 memcpy( &apv->i, yytok.v.s, yytok.slen );
202 YYPARSE_ERROR( "Expected literal (integer or string), but got %s", TOKEN_NAMES[tok] );
206 static void ioparse_regflags( regflags_t flags, unsigned *numBytes )
209 int tok = iolex(TOK_RPAREN);
213 tok = iolex(TOK_INTEGER);
214 flags->fillSizeBytes = 4;
215 ioparse_apval( tok, &flags->fillValue, &flags->fillSizeBytes );
219 READ(TOK_IDENTIFIER);
220 flags->access = yymatch(ACCESS_NAMES,elementsof(ACCESS_NAMES));
221 if( flags->access == -1 ) {
222 YYPARSE_ERROR("Unknown access mode '%s'", yystrdup());
228 tok = iolex(TOK_INTEGER);
229 ioparse_apval( tok, &flags->maskValue, numBytes );
231 YYPARSE_ERROR("mask is not valid on a register block",0);
236 READ(TOK_IDENTIFIER);
237 flags->endian = yymatch(ENDIAN_NAMES,elementsof(ENDIAN_NAMES));
238 if( flags->endian == -1 ) {
239 YYPARSE_ERROR("Unknown endianness '%s'", yystrdup());
244 READ(TOK_IDENTIFIER);
245 flags->traceFlag = yymatch(TRACE_NAMES,elementsof(TRACE_NAMES));
246 if( flags->traceFlag == -1 ) {
247 YYPARSE_ERROR("Unknown trace flag '%s'", yystrdup());
252 READ(TOK_IDENTIFIER);
253 flags->testFlag = yymatch(TRACE_NAMES,elementsof(TRACE_NAMES));
254 if( flags->testFlag == -1 ) {
255 YYPARSE_ERROR("Unknown test flag '%s'", yystrdup());
263 YYPARSE_ERROR("Expected flag or ')' but was %s", TOKEN_NAMES[tok]);
268 static void ioparse_regdef( struct regdef *reg )
270 reg->offset = yytok.v.i;
271 reg->numElements = 1;
273 reg->initValue.i = 0;
274 reg->flags.maskValue.i = -1;
275 unsigned rangeOffset = reg->offset;
277 int tok = iolex(TOK_COLON);
278 if( tok == TOK_RANGE ) {
280 rangeOffset = yytok.v.i;
281 if( rangeOffset < reg->offset ) {
282 YYPARSE_ERROR( "Range end (0x%x) must be greater than the range start (0x%x)", rangeOffset, reg->offset );
285 } else if( tok != TOK_COLON ) {
286 YYPARSE_ERROR( "Expected ':' but was %s\n", TOKEN_NAMES[tok] );
288 READ(TOK_IDENTIFIER);
289 reg->mode = yymatch(MODE_NAMES, elementsof(MODE_NAMES));
290 if( reg->mode == -1 ) {
291 YYPARSE_ERROR( "Unknown register mode '%s'", yystrdup() );
293 if( reg->mode == REG_MIRROR ) {
294 /* Mirror regions have a target range only */
296 reg->initValue.i = yytok.v.i;
298 tok = iolex(TOK_RANGE);
299 if( tok == TOK_RANGE ) {
301 if( yytok.v.i < reg->initValue.i ) {
302 YYPARSE_ERROR( "Invalid mirror target range 0x%x..0x%x", reg->initValue.i, yytok.v.i );
304 reg->numBytes = yytok.v.i - reg->initValue.i + 1;
305 tok = iolex(TOK_STRIDE);
307 if( tok == TOK_STRIDE ) {
309 reg->stride = yytok.v.i;
310 tok = iolex(TOK_ACTION);
312 reg->stride = reg->numBytes;
315 if( tok != TOK_SEMI ) {
316 YYPARSE_ERROR( "Expected ; but gut %s", TOKEN_NAMES[tok] );
319 if( reg->stride < reg->numBytes ) {
320 YYPARSE_ERROR( "Invalid mirror stride: %x is less than block size %x\n", reg->stride, reg->numBytes );
323 if( rangeOffset != reg->offset ) {
324 reg->numElements *= ((rangeOffset - reg->offset) + reg->stride - 1) / reg->stride;
328 READ(TOK_IDENTIFIER);
329 reg->type = yymatch(TYPE_NAMES, elementsof(TYPE_NAMES));
330 if( reg->type == -1 ) {
331 YYPARSE_ERROR( "Unknown register type '%s'", yystrdup() );
333 reg->numBytes = TYPE_SIZES[reg->type];
334 tok = iolex(TOK_IDENTIFIER);
335 if( tok == TOK_IDENTIFIER ) {
336 reg->name = yystrdup();
337 tok = iolex(TOK_ACTION);
339 if( tok == TOK_STRING ) {
340 reg->description = yytok.v.s;
341 tok = iolex(TOK_ACTION);
343 if( tok == TOK_LPAREN ) {
344 ioparse_regflags(®->flags, ®->numBytes);
345 tok = iolex(TOK_ACTION);
347 if( tok == TOK_EQUALS ) {
348 tok = iolex(TOK_INTEGER);
349 if( tok == TOK_UNDEFINED ) {
350 reg->initValue.i = 0xDEADBEEFDEADBEEF;
351 reg->initUndefined = TRUE;
353 ioparse_apval(tok, ®->initValue, ®->numBytes);
355 tok = iolex(TOK_ACTION);
356 } else if( reg->type == REG_STRING ) {
357 YYPARSE_ERROR( "String declarations must have an initializer (ie = 'abcd')",0 );
359 if( tok == TOK_ACTION ) {
360 // reg->action = yystrdup();
361 } else if( tok != TOK_SEMI ) {
362 YYPARSE_ERROR( "Expected ; or {, but got %s", TOKEN_NAMES[tok] );
365 if( rangeOffset != reg->offset ) {
366 reg->numElements *= ((rangeOffset - reg->offset) + reg->numBytes - 1) / reg->numBytes;
372 static struct regblock *ioparse_regblock( )
374 unsigned regsAllocSize = 128;
375 struct regblock *block = g_malloc0(sizeof(struct regblock) + sizeof(regdef_t)*regsAllocSize);
378 READ(TOK_IDENTIFIER);
379 block->name = yystrdup();
380 int tok = iolex(TOK_AT);
381 if( tok == TOK_STRING ) {
382 block->description = yytok.v.s;
385 if( tok != TOK_AT ) {
386 YYPARSE_ERROR("Expected AT but got %s\n", TOKEN_NAMES[tok] );
389 block->address = yytok.v.i;
391 tok = iolex(TOK_LBRACE);
392 if( tok == TOK_LPAREN) {
393 ioparse_regflags(&block->flags, NULL);
395 } else if( tok != TOK_LBRACE ) {
396 YYPARSE_ERROR("Expected { but got %s\n", TOKEN_NAMES[tok] );
399 tok = iolex(TOK_INTEGER);
400 while( tok != TOK_RBRACE ) {
401 if( tok != TOK_INTEGER ) {
402 YYPARSE_ERROR("Expected INTEGER or } but got %s\n", TOKEN_NAMES[tok]);
404 struct regdef *regdef = g_malloc0(sizeof(struct regdef));
405 if( block->numRegs >= regsAllocSize ) {
407 block = g_realloc(block, sizeof(struct regblock) + sizeof(regdef_t)*regsAllocSize);
409 block->regs[block->numRegs++] = regdef;
410 ioparse_regdef(regdef);
412 tok = iolex(TOK_INTEGER);
415 qsort( &block->regs[0], block->numRegs, sizeof(block->regs[0]), register_ptr_sort_cb );
420 GList *ioparse( const char *filename, GList *list )
422 GList *blocks = list;
425 if( iolex_open(filename) != 0 )
430 tok = iolex(TOK_REGISTERS);
431 if( tok == TOK_EOF ) {
433 } else if( tok == TOK_INCLUDE) {
436 } else if( tok == TOK_SPACE ) {
437 } else if( tok == TOK_REGISTERS ) {
438 struct regblock *block = ioparse_regblock(block);
440 blocks = g_list_insert_sorted(blocks, block, register_block_sort_cb);
442 YYPARSE_ERROR("Expected REGISTERS but got %s\n", TOKEN_NAMES[tok] );
444 } while( tok != TOK_EOF );
448 fprintf( stderr, "Warning: input file '%s' did not contain any register definitions\n" );
454 /**************************** Lexical analyser ***************************/
456 static int iolex_open( const char *filename )
459 int fd = open( filename, O_RDONLY );
461 fprintf( stderr, "Error opening file '%s': %s\n", filename, strerror(errno) );
465 if( fstat( fd, &st ) != 0 ) {
466 fprintf( stderr, "Error statting file '%s': %s\n", filename, strerror(errno) );
471 char *data = g_malloc( st.st_size + 1 );
472 if( read(fd, data, st.st_size) != st.st_size ) {
473 fprintf( stderr, "Error reading file '%s': %s\n", filename, strerror(errno) );
478 data[st.st_size] = 0;
480 yybuffer = yyposn = data;
481 yyend = data + st.st_size;
487 static void iolex_close()
490 yybuffer = yyend = NULL;
493 #define YYRETURN(x) do{ \
494 yytok.yylength = yyposn - yystart; \
498 static int iolex_readhex(char *p, int digits)
501 for( int i=0; i < digits; i++ ) {
503 if( isdigit(p[i]) ) {
505 } else if( p[i] >= 'a' && p[i] <= 'f' ) {
506 result += p[i]-'a'+10;
507 } else if( p[i] >= 'A' && p[i] <= 'F' ) {
508 result += p[i]-'A'+10;
510 return (result >> 4);
517 static int iolex_readoctal(char *p, int digits)
520 for( int i=0; i < digits; i++ ) {
522 if( p[i] >= '0' && p[i] <= '7' ) {
525 return (result >> 4);
532 * Copy and interpret the string segment as a C string, replacing escape
533 * expressions with the corresponding value.
535 static char *iolex_getcstring( char *start, char *end, int *len )
537 char *result = g_malloc0(end-start+1); /* Allocate enough memory for the string as-is */
540 for( char *p = start; p != end; p++ ) {
546 if( p[0] >= '0' && p[0] <= '3' && p+3 <= end &&
547 p[1] >= '0' && p[1] <= '7' &&
548 p[2] >= '0' && p[2] <= '7' ) {
549 *q++ = (char)iolex_readoctal(p,3);
563 if( p + 3 > end || !isxdigit(p[1]) || !isxdigit(p[2]) ) {
567 *q++ = (char)iolex_readhex(p+1, 2);
584 int iolex( int expectToken )
587 while( yyposn < yyend ) {
588 char *yystart = yytok.yytext = yyposn;
590 yytok.yyline = yyline;
591 yytok.yycol = yyposn - yylineposn+1;
596 if( *yyposn == 'x' ) {
597 while( yyposn < yyend && isxdigit(*++yyposn) ) ;
599 while( yyposn < yyend && *yyposn >= '0' && *yyposn <= '7' )
603 while( yyposn < yyend && isdigit(*yyposn) )
606 yytok.v.i = strtol( yystart, NULL, 0 );
607 YYRETURN(TOK_INTEGER);
608 } else if( isalpha(ch) || ch == '_' ) {
610 while( yyposn < yyend && (isalnum(*yyposn) || *yyposn == '_') )
612 yytok.yylength = yyposn - yystart;
613 if( expectToken == TOK_IDENTIFIER ) {
614 YYRETURN(TOK_IDENTIFIER);
616 /* Otherwise check for keywords */
617 for( int i=FIRST_KEYWORD; i <= LAST_KEYWORD; i++ ) {
618 if( strlen(TOKEN_NAMES[i]) == yytok.yylength &&
619 strncasecmp(TOKEN_NAMES[i], yystart, yytok.yylength ) == 0 ) {
623 YYRETURN(TOK_IDENTIFIER);
624 } else if( isspace(ch) ) {
629 while( isspace(*yyposn) ) {
630 if( *yyposn == '\n' ) {
632 yylineposn = yyposn+1;
638 case '(': YYRETURN(TOK_LPAREN);
639 case ')': YYRETURN(TOK_RPAREN);
640 case '[': YYRETURN(TOK_LSQUARE);
641 case ']': YYRETURN(TOK_RSQUARE);
642 case ',': YYRETURN(TOK_COMMA);
643 case ':': YYRETURN(TOK_COLON);
644 case ';': YYRETURN(TOK_SEMI);
645 case '=': YYRETURN(TOK_EQUALS);
647 if( *yyposn == '/' ) { /* Line comment */
648 while( yyposn < yyend && *++yyposn != '\n' ) ;
649 } else if( *yyposn == '*' ) { /* C comment */
650 while( yyposn < yyend && (*++yyposn != '*' || *++yyposn != '/' ) ) {
651 if( *yyposn == '\n' ) {
653 yylineposn = yyposn+1;
658 case '\'': /* STRING */
659 while( *yyposn != '\'' ) {
660 if( *yyposn == '\n' ) {
661 fprintf( stderr, "Unexpected newline in string constant!\n" );
663 } else if( yyposn >= yyend ) {
664 fprintf( stderr, "Unexpected EOF in string constant!\n" );
666 } else if( *yyposn == '\\' && yyposn[1] == '\'' ) {
672 yytok.v.s = iolex_getcstring(yystart+1, yyposn-1, &yytok.slen);
673 YYRETURN(TOK_STRING);
674 case '\"': /* STRING */
675 while( *yyposn != '\"' ) {
676 if( *yyposn == '\n' ) {
677 fprintf( stderr, "Unexpected newline in string constant!\n" );
679 } else if( yyposn >= yyend ) {
680 fprintf( stderr, "Unexpected EOF in string constant!\n" );
682 } else if( *yyposn == '\\' && yyposn[1] == '\"' ) {
688 yytok.v.s = iolex_getcstring(yystart+1, yyposn-1, &yytok.slen);
689 YYRETURN(TOK_STRING);
691 YYRETURN(TOK_RBRACE);
692 case '{': /* ACTION or LBRACE */
693 if( expectToken == TOK_LBRACE ) {
694 YYRETURN(TOK_LBRACE);
697 while( count > 0 && yyposn < yyend ) {
704 YYRETURN(TOK_ACTION);
707 if( *yyposn == '.' ) {
711 YYRETURN(TOK_PERIOD);
714 fprintf( stderr, "Illegal character: '%c'\n", ch );
.