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