Search
lxdream.org :: lxdream/src/tools/genmach.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/tools/genmach.c
changeset 1104:700e16c321e5
next1296:30ecee61f811
author nkeynes
date Fri Mar 02 23:49:10 2012 +1000 (8 years ago)
permissions -rw-r--r--
last change Android WIP:
* Rename gui_jni.c to gui_android.c - now quite android specific.
* Implement generic EGL driver with very minimal Java wrapper
* Run emulation in separate thread, and implement simple queue for
inter-thread communication.
* Add menu/action-bar items for start + reset
view annotate diff log raw
     1 /**
     2  * $Id$
     3  *
     4  * mmio register code generator
     5  *
     6  * Copyright (c) 2010 Nathan Keynes.
     7  *
     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.
    12  *
    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.
    17  */
    19 #include <stdio.h>
    20 #include <stdlib.h>
    21 #include <string.h>
    22 #include <unistd.h>
    23 #include <getopt.h>
    24 #include <ctype.h>
    25 #include <errno.h>
    26 #include <glib/gstrfuncs.h>
    27 #include <glib/glist.h>
    28 #include "gettext.h"
    29 #include "genmach.h"
    31 #define LXDREAM_PAGE_SIZE 4096
    33 const char *short_options = "cd:ho:v";
    34 struct option long_options[] = {
    35         { "output", required_argument, NULL, 'o' },
    36         { "header", required_argument, NULL, 'd' },
    37         { "check-only", no_argument, NULL, 'c' },
    38         { "help", no_argument, NULL, 'h' },
    39         { "verbose", no_argument, NULL, 'v' },
    40         { NULL, 0, 0, 0 } };
    42 static void print_version()
    43 {
    44     printf( "genmmio 0.1\n" );
    45 }
    47 static void print_usage()
    48 {
    49     print_version();
    50     printf( "Usage: genmmio [options] <input-register-files>\n" );
    51     printf( "Options:\n" );
    52     printf( "   -c, --check-only       %s\n", _("Check specification files but don't write any output") );
    53     printf( "   -d, --header=FILE      %s\n", _("Specify header output file [corresponding .h for .c file]") );
    54     printf( "   -h, --help             %s\n", _("Display this usage information") );
    55     printf( "   -o, --output=FILE      %s\n", _("Specify main output file [corresponding .c for input file]") );
    56     printf( "   -v, --verbose          %s\n", _("Print verbose output") );
    57 }
    59 /**
    60  * Given an input baseName of the form "path/file.blah", return
    61  * a newly allocated string "file<ext>" where <ext> is the second
    62  * parameter.
    63  *
    64  * @param baseName a non-null filename
    65  * @param ext a file extension including leading '.'
    66  */
    67 char *makeFilename( const char *baseName, const char *ext )
    68 {
    69     const char *p = strrchr(baseName, '/');
    70     if( p == NULL )
    71         p = baseName;
    72     const char *q = strchr(p, '.');
    73     if( q == NULL ) {
    74         return g_strdup_printf("%s%s", p, ext);
    75     } else {
    76         return g_strdup_printf("%.*s%s", q-p, p, ext );
    77     }
    78 }
    80 /**
    81  * Process all registers after parsing is complete. This does a few things:
    82  *   1. Ensures that there are no overlapping registers, so that every operation
    83  *      is well-defined.
    84  *   2. Replaces transitive mirror groups with a set of equivalent direct
    85  *      mirrors so that later processing doesn't need to check for this case.
    86  *      This also checks for cycles in the graph.
    87  *   3. Organises the memory map(s) into page-size quantities (combining and
    88  *      splitting groups where necessary).
    89  */
    90 GList *postprocess_registers( GList *blocks )
    91 {
    92     for( GList *ptr = blocks; ptr != NULL; ptr = ptr->next ) {
    93         regblock_t block = (regblock_t)ptr->data;
    94         for( unsigned i=0; i<block->numRegs; i++ ) {
    95             regdef_t reg = block->regs[i];
    96             if( reg->mode == REG_MIRROR ) {
    97             }
    98         }
    99     }
   100     return blocks;
   101 }
   103 /**
   104  * Check register definition semantics. Primarily this verifies that there
   105  * are no overlapping definitions.
   106  * @return number of errors found.
   107  */
   108 int check_registers( GList *registers )
   109 {
   110     return 0;
   111 }
   113 /**
   114  * Dump the high-level register map to the given file.
   115  */
   116 void dump_register_blocks( FILE *f, GList *blocks, gboolean detail )
   117 {
   118     const char *mode_names[] = { "--", "RW", "RO", "RW" };
   119     for( GList *ptr = blocks; ptr != NULL; ptr=ptr->next ) {
   120         regblock_t block = (regblock_t)ptr->data;
   121         fprintf( f, "%08X: %s - %s\n", block->address,
   122                  block->name,
   123                  (block->description == NULL ? "<no description>" : block->description) );
   124         if( detail ) {
   125             for( unsigned i=0; i<block->numRegs; i++ ) {
   126                 regdef_t reg = block->regs[i];
   127                 fprintf( f, "    %04X: %s %s", reg->offset,
   128                         mode_names[reg->mode],
   129                         reg->name == NULL ? "<anon>" : reg->name );
   130                 if( reg->numElements > 1 ) {
   131                     fprintf( f, "[%d]", reg->numElements );
   132                 }
   133                 fprintf( f, " - %s\n",
   134                          (reg->description == NULL ? "<no description>" : reg->description) );
   135             }
   136         }
   137     }
   138 }
   140 char *build_page_initializer( regblock_t block )
   141 {
   142     char *page = g_malloc(LXDREAM_PAGE_SIZE);
   144     /* First, background fill if any */
   145     if( block->flags.fillSizeBytes == 0 ) {
   146         memset(page, 0, LXDREAM_PAGE_SIZE);
   147     } else {
   148         for( unsigned i=0; i<LXDREAM_PAGE_SIZE; i++ ) {
   149             page[i] = block->flags.fillValue.a[i%block->flags.fillSizeBytes];
   150         }
   151     }
   153     /* Next, set register initializer (except for wo + mirror regs) */
   154     for( unsigned i=0; i<block->numRegs; i++ ) {
   155         regdef_t reg = block->regs[i];
   156         if( reg->mode != REG_WO && reg->mode != REG_MIRROR ) {
   157             unsigned offset = reg->offset;
   158             for( unsigned j=0; j<reg->numElements; j++ ) {
   159                 if( reg->type == REG_STRING ) {
   160                     memcpy( &page[offset], reg->initValue.s, reg->numBytes );
   161                 } else {
   162                     memcpy( &page[offset], reg->initValue.a, reg->numBytes );
   163                 }
   164                 offset += reg->numBytes;
   165             }
   166         }
   167     }
   169     /* Finally clone mirror groups */
   170     for( unsigned i=0; i<block->numRegs; i++ ) {
   171         regdef_t reg = block->regs[i];
   172         if( reg->mode == REG_MIRROR ) {
   173             unsigned offset = reg->offset;
   174             unsigned target = reg->initValue.i;
   175             for( unsigned i=0; i<reg->numElements; i++ ) {
   176                 memcpy( &page[offset], &page[target], reg->numBytes );
   177                 offset += reg->stride;
   178             }
   179         }
   180     }
   182     return page;
   183 }
   186 void fwrite_dump( unsigned char *data, unsigned int length, FILE *f )
   187 {
   188     unsigned int i, j;
   189     int skipped = 0;
   190     for( i =0; i<length; i+=16 ) {
   191         if( i >= 16 && i < length-16 && memcmp( &data[i], &data[i-16], 16) == 0 ) {
   192             skipped++;
   193             continue;
   194         }
   195         if( skipped ) {
   196             fprintf( f, "   **    \n" );
   197             skipped = 0;
   198         }
   199         fprintf( f, "%08X:", i);
   200         for( j=i; j<i+16; j++ ) {
   201             if( j != i && (j % 4) == 0 )
   202                 fprintf( f, " " );
   203             if( j < length )
   204                 fprintf( f, " %02X", (unsigned int)(data[j]) );
   205             else
   206                 fprintf( f, "   " );
   207         }
   208         fprintf( f, "  " );
   209         for( j=i; j<i+16 && j<length; j++ ) {
   210             fprintf( f, "%c", isprint(data[j]) ? data[j] : '.' );
   211         }
   212         fprintf( f, "\n" );
   213     }
   214 }
   216 static const char *file_top = "/**\n"
   217         " * This file was automatically generated by genmmio, do not edit\n"
   218         " */\n";
   220 void write_source( FILE *f, GList *blocks, const char *header_filename )
   221 {
   222     fputs( file_top, f );
   223     fprintf( f, "\n#include \"%s\"\n\n", header_filename );
   225     for( GList *ptr = blocks; ptr != NULL; ptr = ptr->next ) {
   226         regblock_t block = (regblock_t)ptr->data;
   227         /* Generate the mmio region struct */
   228         fprintf( f, "void FASTCALL mmio_region_%s_write(uint32_t, uint32_t);\n", block->name );
   229         fprintf( f, "void FASTCALL mmio_region_%s_write_word(uint32_t, uint32_t);\n", block->name );
   230         fprintf( f, "void FASTCALL mmio_region_%s_write_byte(uint32_t, uint32_t);\n", block->name );
   231         fprintf( f, "void FASTCALL mmio_region_%s_write_burst(uint32_t, unsigned char *);\n", block->name );
   232         fprintf( f, "int32_t FASTCALL mmio_region_%s_read(uint32_t);\n", block->name );
   233         fprintf( f, "int32_t FASTCALL mmio_region_%s_read_word(uint32_t);\n", block->name );
   234         fprintf( f, "int32_t FASTCALL mmio_region_%s_read_byte(uint32_t);\n", block->name );
   235         fprintf( f, "void FASTCALL mmio_region_%s_read_burst(unsigned char *, uint32_t);\n", block->name );
   236         fprintf( f, "struct mmio_region mmio_region_%s = {\n", block->name );
   237         fprintf( f, "    \"%s\", \"%s\", %08x, {\n", block->name,
   238                 block->description == NULL ? block->name : block->description,
   239                 block->address );
   240         fprintf( f, "        mmio_region_%s_read, mmio_region_%s_write,\n", block->name, block->name );
   241         fprintf( f, "        mmio_region_%s_read_word, mmio_region_%s_write_word,\n", block->name, block->name );
   242         fprintf( f, "        mmio_region_%s_read_byte, mmio_region_%s_write_byte,\n", block->name, block->name );
   243         fprintf( f, "        mmio_region_%s_read_burst, mmio_region_%s_write_burst,\n", block->name, block->name );
   244         fprintf( f, "        unmapped_prefetch, mmio_region_%s_read_byte },\n", block->name, block->name );
   245         fprintf( f, "    NULL, NULL, {\n" );
   246         for( unsigned i=0; i<block->numRegs; i++ ) {
   247             regdef_t reg = block->regs[i];
   248             if( reg->mode != REG_CONST ) {
   249                 char tmp[32];
   250                 const char *regname = reg->name;
   251                 if( regname == NULL ) {
   252                     regname = tmp;
   253                     snprintf( tmp, sizeof(tmp), "%s_%03X", block->name, reg->offset );
   254                 }
   256                 fprintf( f, "        { \"%s\", \"%s\", %d, 0x%03x, 0x%08x, %s },\n", regname,
   257                          reg->description == NULL ? regname : reg->description,
   258                                  reg->numBytes*8, reg->offset, (unsigned)reg->initValue.i,
   259                                  (reg->mode == REG_RW ? "PORT_MRW" : (reg->mode == REG_WO ? "PORT_W" : "PORT_R")) );
   260             }
   261         }
   262         fprintf( f, "    }, NULL };\n" );
   263     }
   265 }
   267 void write_header( FILE *f, GList *blocks )
   268 {
   269     fputs( file_top, f );
   271     fputs( "\n#include \"mmio.h\"\n\n", f );
   273     for( GList *ptr = blocks; ptr != NULL; ptr = ptr->next ) {
   274         regblock_t block = (regblock_t)ptr->data;
   275         fprintf( f, "extern struct mmio_region mmio_region_%s;\n", block->name );
   276         fprintf( f, "enum mmio_region_%s_port_t {\n", block->name );
   277         for( unsigned i=0; i<block->numRegs; i++ ) {
   278             regdef_t reg = block->regs[i];
   279             if( reg->name != NULL ) {
   280                 fprintf( f, "    %s = 0x%03x,\n", reg->name, reg->offset );
   281             }
   282         }
   283         fprintf( f, "};\n" );
   284     }
   285 }
   287 void write_test( FILE *f, GList *blocks )
   288 {
   291 }
   293 int main(int argc, char *argv[])
   294 {
   295     const char *header = NULL;
   296     const char *output = NULL;
   297     gboolean check_only = FALSE;
   298     int verbose = 0, opt;
   299     GList *block_list = NULL;
   301     while( (opt = getopt_long( argc, argv, short_options, long_options, NULL )) != -1 ) {
   302         switch(opt) {
   303         case 'c':
   304             check_only = TRUE;
   305             break;
   306         case 'd':
   307             header = optarg;
   308             break;
   309         case 'h':
   310             print_usage();
   311             exit(0);
   312         case 'o':
   313             output = optarg;
   314             break;
   315         case 'v':
   316             verbose++;
   317             break;
   318         }
   319     }
   321     if( optind == argc ) {
   322         print_usage();
   323         exit(1);
   324     }
   326     if( output == NULL ) {
   327         output = makeFilename(argv[optind],".c");
   328     }
   329     if( header == NULL ) {
   330         header = makeFilename(argv[optind],".h");
   331     }
   333     for( ; optind < argc; optind++ ) {
   334         block_list = ioparse(argv[optind], block_list);
   335     }
   337     if( verbose ) {
   338         dump_register_blocks(stdout, block_list, verbose>1);
   339     }
   341     int errors = check_registers(block_list);
   342     if( errors != 0 ) {
   343         fprintf( stderr, "Aborting due to validation errors\n" );
   344         return 2;
   345     }
   347     if( !check_only ) {
   348         FILE *f = fopen( output, "wo" );
   349         if( f == NULL ) {
   350             fprintf( stderr, "Unable to open output file '%s': %s\n", output, strerror(errno) );
   351             return 3;
   352         }
   353         write_source( f, block_list, header );
   354         fclose(f);
   356         f = fopen( header, "wo" );
   357         if( f == NULL ) {
   358             fprintf( stderr, "Unable to open header file '%s': %s\n", header, strerror(errno) );
   359             return 4;
   360         }
   361         write_header( f, block_list );
   362         fclose(f);
   363     }
   365     for( GList *ptr = block_list; ptr != NULL; ptr = ptr->next ) {
   366         char *data = build_page_initializer((regblock_t)ptr->data);
   367         fwrite_dump( data, LXDREAM_PAGE_SIZE, stdout );
   368         g_free(data);
   369     }
   370     return 0;
   371 }
.