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 {
119 char *yyposn, *yylineposn, *yyend;
123 static GList *yyfile_stack = NULL;
124 static struct yystate yystate;
125 static struct token_data yytok;
127 #define YYPARSE_ERROR( msg, ... ) \
129 fprintf( stderr, "Parse error in %s:%d:%d: " msg "\n", yystate.yyfilename, yytok.yyline, yytok.yycol, __VA_ARGS__ ); \
134 { int _tok = iolex(x); \
135 if( _tok != (x) ) { \
136 YYPARSE_ERROR( "Expected %s but got %s", TOKEN_NAMES[x], TOKEN_NAMES[_tok] ); \
140 static int iolex( int expectToken );
141 static int iolex_open( const char *filename );
142 static void iolex_close();
143 static int iolex_push( const char *filename );
144 static int iolex_pop( );
146 static inline char *yystrdup()
148 char *str = g_malloc0(yytok.yylength+1);
149 memcpy( str, yytok.yytext, yytok.yylength);
153 static inline int yystrcasecmp(const char *cmp)
155 int len = strlen(cmp);
156 if( len != yytok.yylength ) {
157 return yytok.yylength - len;
159 return strncasecmp(yytok.yytext, cmp, yytok.yylength);
162 static int yymatch( const char *arr[], unsigned numOptions )
164 for( unsigned i=0; i<numOptions; i++ ) {
165 if( yystrcasecmp( arr[i] ) == 0 ) {
172 static gint register_block_sort_cb( gconstpointer a, gconstpointer b )
174 struct regblock *blocka = (struct regblock *)a;
175 struct regblock *blockb = (struct regblock *)b;
176 return blocka->address - blockb->address;
179 static int register_ptr_sort_cb( const void *a, const void *b )
181 regdef_t *ptra = (regdef_t *)a;
182 regdef_t *ptrb = (regdef_t *)b;
183 if( (*ptra)->offset != (*ptrb)->offset )
184 return (*ptra)->offset - (*ptrb)->offset;
185 return (*ptra)->type - (*ptrb)->type;
188 static void ioparse_apval(int tok, union apval *apv, unsigned *numBytes)
190 if( tok == TOK_INTEGER ) {
192 if( *numBytes == 0 ) {
193 YYPARSE_ERROR( "Expected string initializer but was an integer (0x%x)", yytok.v.i );
195 } else if( tok = TOK_STRING ) {
196 if( *numBytes == 0 ) {
197 *numBytes = yytok.slen;
200 if( *numBytes != yytok.slen ) {
201 YYPARSE_ERROR( "Expected %d byte initializer but was %d", *numBytes, yytok.slen );
203 assert( *numBytes < sizeof(uint64_t) );
205 /* FIXME: handle endian mismatches */
206 memcpy( &apv->i, yytok.v.s, yytok.slen );
209 YYPARSE_ERROR( "Expected literal (integer or string), but got %s", TOKEN_NAMES[tok] );
213 static void ioparse_regflags( regflags_t flags, unsigned *numBytes )
216 int tok = iolex(TOK_RPAREN);
220 tok = iolex(TOK_INTEGER);
221 flags->fillSizeBytes = 4;
222 ioparse_apval( tok, &flags->fillValue, &flags->fillSizeBytes );
226 READ(TOK_IDENTIFIER);
227 flags->access = yymatch(ACCESS_NAMES,elementsof(ACCESS_NAMES));
228 if( flags->access == -1 ) {
229 YYPARSE_ERROR("Unknown access mode '%s'", yystrdup());
235 tok = iolex(TOK_INTEGER);
236 ioparse_apval( tok, &flags->maskValue, numBytes );
238 YYPARSE_ERROR("mask is not valid on a register block",0);
243 READ(TOK_IDENTIFIER);
244 flags->endian = yymatch(ENDIAN_NAMES,elementsof(ENDIAN_NAMES));
245 if( flags->endian == -1 ) {
246 YYPARSE_ERROR("Unknown endianness '%s'", yystrdup());
251 READ(TOK_IDENTIFIER);
252 flags->traceFlag = yymatch(TRACE_NAMES,elementsof(TRACE_NAMES));
253 if( flags->traceFlag == -1 ) {
254 YYPARSE_ERROR("Unknown trace flag '%s'", yystrdup());
259 READ(TOK_IDENTIFIER);
260 flags->testFlag = yymatch(TRACE_NAMES,elementsof(TRACE_NAMES));
261 if( flags->testFlag == -1 ) {
262 YYPARSE_ERROR("Unknown test flag '%s'", yystrdup());
270 YYPARSE_ERROR("Expected flag or ')' but was %s", TOKEN_NAMES[tok]);
275 static void ioparse_regdef( struct regdef *reg )
277 reg->offset = yytok.v.i;
278 reg->numElements = 1;
280 reg->initValue.i = 0;
281 reg->flags.maskValue.i = -1;
282 unsigned rangeOffset = reg->offset;
284 int tok = iolex(TOK_COLON);
285 if( tok == TOK_RANGE ) {
287 rangeOffset = yytok.v.i;
288 if( rangeOffset < reg->offset ) {
289 YYPARSE_ERROR( "Range end (0x%x) must be greater than the range start (0x%x)", rangeOffset, reg->offset );
292 } else if( tok != TOK_COLON ) {
293 YYPARSE_ERROR( "Expected ':' but was %s\n", TOKEN_NAMES[tok] );
295 READ(TOK_IDENTIFIER);
296 reg->mode = yymatch(MODE_NAMES, elementsof(MODE_NAMES));
297 if( reg->mode == -1 ) {
298 YYPARSE_ERROR( "Unknown register mode '%s'", yystrdup() );
300 if( reg->mode == REG_MIRROR ) {
301 /* Mirror regions have a target range only */
303 reg->initValue.i = yytok.v.i;
305 tok = iolex(TOK_RANGE);
306 if( tok == TOK_RANGE ) {
308 if( yytok.v.i < reg->initValue.i ) {
309 YYPARSE_ERROR( "Invalid mirror target range 0x%x..0x%x", reg->initValue.i, yytok.v.i );
311 reg->numBytes = yytok.v.i - reg->initValue.i + 1;
312 tok = iolex(TOK_STRIDE);
314 if( tok == TOK_STRIDE ) {
316 reg->stride = yytok.v.i;
317 tok = iolex(TOK_ACTION);
319 reg->stride = reg->numBytes;
322 if( tok != TOK_SEMI ) {
323 YYPARSE_ERROR( "Expected ; but gut %s", TOKEN_NAMES[tok] );
326 if( reg->stride < reg->numBytes ) {
327 YYPARSE_ERROR( "Invalid mirror stride: %x is less than block size %x\n", reg->stride, reg->numBytes );
330 if( rangeOffset != reg->offset ) {
331 reg->numElements *= ((rangeOffset - reg->offset) + reg->stride - 1) / reg->stride;
335 READ(TOK_IDENTIFIER);
336 reg->type = yymatch(TYPE_NAMES, elementsof(TYPE_NAMES));
337 if( reg->type == -1 ) {
338 YYPARSE_ERROR( "Unknown register type '%s'", yystrdup() );
340 reg->numBytes = TYPE_SIZES[reg->type];
341 tok = iolex(TOK_IDENTIFIER);
342 if( tok == TOK_IDENTIFIER ) {
343 reg->name = yystrdup();
344 tok = iolex(TOK_ACTION);
346 if( tok == TOK_STRING ) {
347 reg->description = yytok.v.s;
348 tok = iolex(TOK_ACTION);
350 if( tok == TOK_LPAREN ) {
351 ioparse_regflags(®->flags, ®->numBytes);
352 tok = iolex(TOK_ACTION);
354 if( tok == TOK_EQUALS ) {
355 tok = iolex(TOK_INTEGER);
356 if( tok == TOK_UNDEFINED ) {
357 reg->initValue.i = 0xDEADBEEFDEADBEEF;
358 reg->initUndefined = TRUE;
360 ioparse_apval(tok, ®->initValue, ®->numBytes);
362 tok = iolex(TOK_ACTION);
363 } else if( reg->type == REG_STRING ) {
364 YYPARSE_ERROR( "String declarations must have an initializer (ie = 'abcd')",0 );
366 if( tok == TOK_ACTION ) {
367 // reg->action = yystrdup();
368 } else if( tok != TOK_SEMI ) {
369 YYPARSE_ERROR( "Expected ; or {, but got %s", TOKEN_NAMES[tok] );
372 if( rangeOffset != reg->offset ) {
373 reg->numElements *= ((rangeOffset - reg->offset) + reg->numBytes - 1) / reg->numBytes;
379 static struct regblock *ioparse_regblock( )
381 unsigned regsAllocSize = 128;
382 struct regblock *block = g_malloc0(sizeof(struct regblock) + sizeof(regdef_t)*regsAllocSize);
385 READ(TOK_IDENTIFIER);
386 block->name = yystrdup();
387 int tok = iolex(TOK_AT);
388 if( tok == TOK_STRING ) {
389 block->description = yytok.v.s;
392 if( tok != TOK_AT ) {
393 YYPARSE_ERROR("Expected AT but got %s\n", TOKEN_NAMES[tok] );
396 block->address = yytok.v.i;
398 tok = iolex(TOK_LBRACE);
399 if( tok == TOK_LPAREN) {
400 ioparse_regflags(&block->flags, NULL);
402 } else if( tok != TOK_LBRACE ) {
403 YYPARSE_ERROR("Expected { but got %s\n", TOKEN_NAMES[tok] );
406 tok = iolex(TOK_INTEGER);
407 while( tok != TOK_RBRACE ) {
408 if( tok != TOK_INTEGER ) {
409 YYPARSE_ERROR("Expected INTEGER or } but got %s\n", TOKEN_NAMES[tok]);
411 struct regdef *regdef = g_malloc0(sizeof(struct regdef));
412 if( block->numRegs >= regsAllocSize ) {
414 block = g_realloc(block, sizeof(struct regblock) + sizeof(regdef_t)*regsAllocSize);
416 block->regs[block->numRegs++] = regdef;
417 ioparse_regdef(regdef);
419 tok = iolex(TOK_INTEGER);
422 qsort( &block->regs[0], block->numRegs, sizeof(block->regs[0]), register_ptr_sort_cb );
427 GList *ioparse( const char *filename, GList *list )
429 GList *blocks = list;
432 if( iolex_open(filename) != 0 )
437 tok = iolex(TOK_REGISTERS);
438 if( tok == TOK_EOF ) {
439 int result = iolex_pop();
442 } else if( tok == TOK_INCLUDE) {
444 char *tmp = yystrdup();
446 int result = iolex_push( tmp );
448 YYPARSE_ERROR("Unable to include file '%s'", tmp);
451 } else if( tok == TOK_SPACE ) {
452 } else if( tok == TOK_REGISTERS ) {
453 struct regblock *block = ioparse_regblock(block);
455 blocks = g_list_insert_sorted(blocks, block, register_block_sort_cb);
457 YYPARSE_ERROR("Expected REGISTERS but got %s\n", TOKEN_NAMES[tok] );
463 /**************************** Lexical analyser ***************************/
465 static int iolex_push( const char *filename )
467 struct yystate *save = g_malloc(sizeof(struct yystate));
468 memcpy( save, &yystate, sizeof(struct yystate) );
470 int result = iolex_open(filename);
472 yyfile_stack = g_list_prepend(yyfile_stack, save);
477 static int iolex_pop( )
480 if( yyfile_stack == NULL )
482 struct yystate *top = (struct yystate *)yyfile_stack->data;
483 yyfile_stack = g_list_remove(yyfile_stack, top);
484 memcpy( &yystate, top, sizeof(struct yystate) );
489 static int iolex_open( const char *filename )
492 int fd = open( filename, O_RDONLY );
494 fprintf( stderr, "Error opening file '%s': %s\n", filename, strerror(errno) );
498 if( fstat( fd, &st ) != 0 ) {
499 fprintf( stderr, "Error statting file '%s': %s\n", filename, strerror(errno) );
504 char *data = g_malloc( st.st_size + 1 );
505 if( read(fd, data, st.st_size) != st.st_size ) {
506 fprintf( stderr, "Error reading file '%s': %s\n", filename, strerror(errno) );
511 data[st.st_size] = 0;
513 yystate.yybuffer = yystate.yyposn = data;
514 yystate.yyend = data + st.st_size;
515 yystate.yylineno = 1;
516 yystate.yylineposn = yystate.yyposn;
517 yystate.yyfilename = strdup(filename);
521 static void iolex_close()
523 g_free(yystate.yybuffer);
524 free(yystate.yyfilename);
525 memset(&yystate, 0, sizeof(struct yystate));
528 #define YYRETURN(x) do{ \
529 yytok.yylength = yystate.yyposn - yystart; \
533 static int iolex_readhex(char *p, int digits)
536 for( int i=0; i < digits; i++ ) {
538 if( isdigit(p[i]) ) {
540 } else if( p[i] >= 'a' && p[i] <= 'f' ) {
541 result += p[i]-'a'+10;
542 } else if( p[i] >= 'A' && p[i] <= 'F' ) {
543 result += p[i]-'A'+10;
545 return (result >> 4);
552 static int iolex_readoctal(char *p, int digits)
555 for( int i=0; i < digits; i++ ) {
557 if( p[i] >= '0' && p[i] <= '7' ) {
560 return (result >> 4);
567 * Copy and interpret the string segment as a C string, replacing escape
568 * expressions with the corresponding value.
570 static char *iolex_getcstring( char *start, char *end, int *len )
572 char *result = g_malloc0(end-start+1); /* Allocate enough memory for the string as-is */
575 for( char *p = start; p != end; p++ ) {
581 if( p[0] >= '0' && p[0] <= '3' && p+3 <= end &&
582 p[1] >= '0' && p[1] <= '7' &&
583 p[2] >= '0' && p[2] <= '7' ) {
584 *q++ = (char)iolex_readoctal(p,3);
598 if( p + 3 > end || !isxdigit(p[1]) || !isxdigit(p[2]) ) {
602 *q++ = (char)iolex_readhex(p+1, 2);
619 int iolex( int expectToken )
622 while( yystate.yyposn < yystate.yyend ) {
623 char *yystart = yytok.yytext = yystate.yyposn;
625 yytok.yyline = yystate.yylineno;
626 yytok.yycol = yystate.yyposn - yystate.yylineposn+1;
627 int ch = *yystate.yyposn++;
631 if( *yystate.yyposn == 'x' ) {
632 while( yystate.yyposn < yystate.yyend && isxdigit(*++yystate.yyposn) ) ;
634 while( yystate.yyposn < yystate.yyend && *yystate.yyposn >= '0' && *yystate.yyposn <= '7' )
638 while( yystate.yyposn < yystate.yyend && isdigit(*yystate.yyposn) )
641 yytok.v.i = strtol( yystart, NULL, 0 );
642 YYRETURN(TOK_INTEGER);
643 } else if( isalpha(ch) || ch == '_' ) {
645 while( yystate.yyposn < yystate.yyend && (isalnum(*yystate.yyposn) || *yystate.yyposn == '_') )
647 yytok.yylength = yystate.yyposn - yystart;
648 if( expectToken == TOK_IDENTIFIER ) {
649 YYRETURN(TOK_IDENTIFIER);
651 /* Otherwise check for keywords */
652 for( int i=FIRST_KEYWORD; i <= LAST_KEYWORD; i++ ) {
653 if( strlen(TOKEN_NAMES[i]) == yytok.yylength &&
654 strncasecmp(TOKEN_NAMES[i], yystart, yytok.yylength ) == 0 ) {
658 YYRETURN(TOK_IDENTIFIER);
659 } else if( isspace(ch) ) {
662 yystate.yylineposn = yystate.yyposn;
664 while( isspace(*yystate.yyposn) ) {
665 if( *yystate.yyposn == '\n' ) {
667 yystate.yylineposn = yystate.yyposn+1;
673 case '(': YYRETURN(TOK_LPAREN);
674 case ')': YYRETURN(TOK_RPAREN);
675 case '[': YYRETURN(TOK_LSQUARE);
676 case ']': YYRETURN(TOK_RSQUARE);
677 case ',': YYRETURN(TOK_COMMA);
678 case ':': YYRETURN(TOK_COLON);
679 case ';': YYRETURN(TOK_SEMI);
680 case '=': YYRETURN(TOK_EQUALS);
682 if( *yystate.yyposn == '/' ) { /* Line comment */
683 while( yystate.yyposn < yystate.yyend && *++yystate.yyposn != '\n' ) ;
684 } else if( *yystate.yyposn == '*' ) { /* C comment */
685 while( yystate.yyposn < yystate.yyend && (*++yystate.yyposn != '*' || *++yystate.yyposn != '/' ) ) {
686 if( *yystate.yyposn == '\n' ) {
688 yystate.yylineposn = yystate.yyposn+1;
693 case '\'': /* STRING */
694 while( *yystate.yyposn != '\'' ) {
695 if( *yystate.yyposn == '\n' ) {
696 fprintf( stderr, "Unexpected newline in string constant!\n" );
698 } else if( yystate.yyposn >= yystate.yyend ) {
699 fprintf( stderr, "Unexpected EOF in string constant!\n" );
701 } else if( *yystate.yyposn == '\\' && yystate.yyposn[1] == '\'' ) {
707 yytok.v.s = iolex_getcstring(yystart+1, yystate.yyposn-1, &yytok.slen);
708 YYRETURN(TOK_STRING);
709 case '\"': /* STRING */
710 while( *yystate.yyposn != '\"' ) {
711 if( *yystate.yyposn == '\n' ) {
712 fprintf( stderr, "Unexpected newline in string constant!\n" );
714 } else if( yystate.yyposn >= yystate.yyend ) {
715 fprintf( stderr, "Unexpected EOF in string constant!\n" );
717 } else if( *yystate.yyposn == '\\' && yystate.yyposn[1] == '\"' ) {
723 yytok.v.s = iolex_getcstring(yystart+1, yystate.yyposn-1, &yytok.slen);
724 YYRETURN(TOK_STRING);
726 YYRETURN(TOK_RBRACE);
727 case '{': /* ACTION or LBRACE */
728 if( expectToken == TOK_LBRACE ) {
729 YYRETURN(TOK_LBRACE);
732 while( count > 0 && yystate.yyposn < yystate.yyend ) {
733 if( *yystate.yyposn == '{' )
735 if( *yystate.yyposn == '}' )
739 YYRETURN(TOK_ACTION);
742 if( *yystate.yyposn == '.' ) {
746 YYRETURN(TOK_PERIOD);
749 fprintf( stderr, "Illegal character: '%c'\n", ch );
.