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