Search
lxdream.org :: lxdream/src/tools/genmach.c :: diff
lxdream 0.9.1
released Jun 29
Download Now
filename src/tools/genmach.c
changeset 1104:700e16c321e5
next1296:30ecee61f811
author nkeynes
date Mon Mar 15 22:10:24 2010 +1000 (12 years ago)
permissions -rw-r--r--
last change Commit genmach work-in-progress
file annotate diff log raw
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/src/tools/genmach.c Mon Mar 15 22:10:24 2010 +1000
1.3 @@ -0,0 +1,372 @@
1.4 +/**
1.5 + * $Id$
1.6 + *
1.7 + * mmio register code generator
1.8 + *
1.9 + * Copyright (c) 2010 Nathan Keynes.
1.10 + *
1.11 + * This program is free software; you can redistribute it and/or modify
1.12 + * it under the terms of the GNU General Public License as published by
1.13 + * the Free Software Foundation; either version 2 of the License, or
1.14 + * (at your option) any later version.
1.15 + *
1.16 + * This program is distributed in the hope that it will be useful,
1.17 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1.18 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.19 + * GNU General Public License for more details.
1.20 + */
1.21 +
1.22 +#include <stdio.h>
1.23 +#include <stdlib.h>
1.24 +#include <string.h>
1.25 +#include <unistd.h>
1.26 +#include <getopt.h>
1.27 +#include <ctype.h>
1.28 +#include <errno.h>
1.29 +#include <glib/gstrfuncs.h>
1.30 +#include <glib/glist.h>
1.31 +#include "gettext.h"
1.32 +#include "genmach.h"
1.33 +
1.34 +#define LXDREAM_PAGE_SIZE 4096
1.35 +
1.36 +const char *short_options = "cd:ho:v";
1.37 +struct option long_options[] = {
1.38 + { "output", required_argument, NULL, 'o' },
1.39 + { "header", required_argument, NULL, 'd' },
1.40 + { "check-only", no_argument, NULL, 'c' },
1.41 + { "help", no_argument, NULL, 'h' },
1.42 + { "verbose", no_argument, NULL, 'v' },
1.43 + { NULL, 0, 0, 0 } };
1.44 +
1.45 +static void print_version()
1.46 +{
1.47 + printf( "genmmio 0.1\n" );
1.48 +}
1.49 +
1.50 +static void print_usage()
1.51 +{
1.52 + print_version();
1.53 + printf( "Usage: genmmio [options] <input-register-files>\n" );
1.54 + printf( "Options:\n" );
1.55 + printf( " -c, --check-only %s\n", _("Check specification files but don't write any output") );
1.56 + printf( " -d, --header=FILE %s\n", _("Specify header output file [corresponding .h for .c file]") );
1.57 + printf( " -h, --help %s\n", _("Display this usage information") );
1.58 + printf( " -o, --output=FILE %s\n", _("Specify main output file [corresponding .c for input file]") );
1.59 + printf( " -v, --verbose %s\n", _("Print verbose output") );
1.60 +}
1.61 +
1.62 +/**
1.63 + * Given an input baseName of the form "path/file.blah", return
1.64 + * a newly allocated string "file<ext>" where <ext> is the second
1.65 + * parameter.
1.66 + *
1.67 + * @param baseName a non-null filename
1.68 + * @param ext a file extension including leading '.'
1.69 + */
1.70 +char *makeFilename( const char *baseName, const char *ext )
1.71 +{
1.72 + const char *p = strrchr(baseName, '/');
1.73 + if( p == NULL )
1.74 + p = baseName;
1.75 + const char *q = strchr(p, '.');
1.76 + if( q == NULL ) {
1.77 + return g_strdup_printf("%s%s", p, ext);
1.78 + } else {
1.79 + return g_strdup_printf("%.*s%s", q-p, p, ext );
1.80 + }
1.81 +}
1.82 +
1.83 +/**
1.84 + * Process all registers after parsing is complete. This does a few things:
1.85 + * 1. Ensures that there are no overlapping registers, so that every operation
1.86 + * is well-defined.
1.87 + * 2. Replaces transitive mirror groups with a set of equivalent direct
1.88 + * mirrors so that later processing doesn't need to check for this case.
1.89 + * This also checks for cycles in the graph.
1.90 + * 3. Organises the memory map(s) into page-size quantities (combining and
1.91 + * splitting groups where necessary).
1.92 + */
1.93 +GList *postprocess_registers( GList *blocks )
1.94 +{
1.95 + for( GList *ptr = blocks; ptr != NULL; ptr = ptr->next ) {
1.96 + regblock_t block = (regblock_t)ptr->data;
1.97 + for( unsigned i=0; i<block->numRegs; i++ ) {
1.98 + regdef_t reg = block->regs[i];
1.99 + if( reg->mode == REG_MIRROR ) {
1.100 + }
1.101 + }
1.102 + }
1.103 + return blocks;
1.104 +}
1.105 +
1.106 +/**
1.107 + * Check register definition semantics. Primarily this verifies that there
1.108 + * are no overlapping definitions.
1.109 + * @return number of errors found.
1.110 + */
1.111 +int check_registers( GList *registers )
1.112 +{
1.113 + return 0;
1.114 +}
1.115 +
1.116 +/**
1.117 + * Dump the high-level register map to the given file.
1.118 + */
1.119 +void dump_register_blocks( FILE *f, GList *blocks, gboolean detail )
1.120 +{
1.121 + const char *mode_names[] = { "--", "RW", "RO", "RW" };
1.122 + for( GList *ptr = blocks; ptr != NULL; ptr=ptr->next ) {
1.123 + regblock_t block = (regblock_t)ptr->data;
1.124 + fprintf( f, "%08X: %s - %s\n", block->address,
1.125 + block->name,
1.126 + (block->description == NULL ? "<no description>" : block->description) );
1.127 + if( detail ) {
1.128 + for( unsigned i=0; i<block->numRegs; i++ ) {
1.129 + regdef_t reg = block->regs[i];
1.130 + fprintf( f, " %04X: %s %s", reg->offset,
1.131 + mode_names[reg->mode],
1.132 + reg->name == NULL ? "<anon>" : reg->name );
1.133 + if( reg->numElements > 1 ) {
1.134 + fprintf( f, "[%d]", reg->numElements );
1.135 + }
1.136 + fprintf( f, " - %s\n",
1.137 + (reg->description == NULL ? "<no description>" : reg->description) );
1.138 + }
1.139 + }
1.140 + }
1.141 +}
1.142 +
1.143 +char *build_page_initializer( regblock_t block )
1.144 +{
1.145 + char *page = g_malloc(LXDREAM_PAGE_SIZE);
1.146 +
1.147 + /* First, background fill if any */
1.148 + if( block->flags.fillSizeBytes == 0 ) {
1.149 + memset(page, 0, LXDREAM_PAGE_SIZE);
1.150 + } else {
1.151 + for( unsigned i=0; i<LXDREAM_PAGE_SIZE; i++ ) {
1.152 + page[i] = block->flags.fillValue.a[i%block->flags.fillSizeBytes];
1.153 + }
1.154 + }
1.155 +
1.156 + /* Next, set register initializer (except for wo + mirror regs) */
1.157 + for( unsigned i=0; i<block->numRegs; i++ ) {
1.158 + regdef_t reg = block->regs[i];
1.159 + if( reg->mode != REG_WO && reg->mode != REG_MIRROR ) {
1.160 + unsigned offset = reg->offset;
1.161 + for( unsigned j=0; j<reg->numElements; j++ ) {
1.162 + if( reg->type == REG_STRING ) {
1.163 + memcpy( &page[offset], reg->initValue.s, reg->numBytes );
1.164 + } else {
1.165 + memcpy( &page[offset], reg->initValue.a, reg->numBytes );
1.166 + }
1.167 + offset += reg->numBytes;
1.168 + }
1.169 + }
1.170 + }
1.171 +
1.172 + /* Finally clone mirror groups */
1.173 + for( unsigned i=0; i<block->numRegs; i++ ) {
1.174 + regdef_t reg = block->regs[i];
1.175 + if( reg->mode == REG_MIRROR ) {
1.176 + unsigned offset = reg->offset;
1.177 + unsigned target = reg->initValue.i;
1.178 + for( unsigned i=0; i<reg->numElements; i++ ) {
1.179 + memcpy( &page[offset], &page[target], reg->numBytes );
1.180 + offset += reg->stride;
1.181 + }
1.182 + }
1.183 + }
1.184 +
1.185 + return page;
1.186 +}
1.187 +
1.188 +
1.189 +void fwrite_dump( unsigned char *data, unsigned int length, FILE *f )
1.190 +{
1.191 + unsigned int i, j;
1.192 + int skipped = 0;
1.193 + for( i =0; i<length; i+=16 ) {
1.194 + if( i >= 16 && i < length-16 && memcmp( &data[i], &data[i-16], 16) == 0 ) {
1.195 + skipped++;
1.196 + continue;
1.197 + }
1.198 + if( skipped ) {
1.199 + fprintf( f, " ** \n" );
1.200 + skipped = 0;
1.201 + }
1.202 + fprintf( f, "%08X:", i);
1.203 + for( j=i; j<i+16; j++ ) {
1.204 + if( j != i && (j % 4) == 0 )
1.205 + fprintf( f, " " );
1.206 + if( j < length )
1.207 + fprintf( f, " %02X", (unsigned int)(data[j]) );
1.208 + else
1.209 + fprintf( f, " " );
1.210 + }
1.211 + fprintf( f, " " );
1.212 + for( j=i; j<i+16 && j<length; j++ ) {
1.213 + fprintf( f, "%c", isprint(data[j]) ? data[j] : '.' );
1.214 + }
1.215 + fprintf( f, "\n" );
1.216 + }
1.217 +}
1.218 +
1.219 +static const char *file_top = "/**\n"
1.220 + " * This file was automatically generated by genmmio, do not edit\n"
1.221 + " */\n";
1.222 +
1.223 +void write_source( FILE *f, GList *blocks, const char *header_filename )
1.224 +{
1.225 + fputs( file_top, f );
1.226 + fprintf( f, "\n#include \"%s\"\n\n", header_filename );
1.227 +
1.228 + for( GList *ptr = blocks; ptr != NULL; ptr = ptr->next ) {
1.229 + regblock_t block = (regblock_t)ptr->data;
1.230 + /* Generate the mmio region struct */
1.231 + fprintf( f, "void FASTCALL mmio_region_%s_write(uint32_t, uint32_t);\n", block->name );
1.232 + fprintf( f, "void FASTCALL mmio_region_%s_write_word(uint32_t, uint32_t);\n", block->name );
1.233 + fprintf( f, "void FASTCALL mmio_region_%s_write_byte(uint32_t, uint32_t);\n", block->name );
1.234 + fprintf( f, "void FASTCALL mmio_region_%s_write_burst(uint32_t, unsigned char *);\n", block->name );
1.235 + fprintf( f, "int32_t FASTCALL mmio_region_%s_read(uint32_t);\n", block->name );
1.236 + fprintf( f, "int32_t FASTCALL mmio_region_%s_read_word(uint32_t);\n", block->name );
1.237 + fprintf( f, "int32_t FASTCALL mmio_region_%s_read_byte(uint32_t);\n", block->name );
1.238 + fprintf( f, "void FASTCALL mmio_region_%s_read_burst(unsigned char *, uint32_t);\n", block->name );
1.239 + fprintf( f, "struct mmio_region mmio_region_%s = {\n", block->name );
1.240 + fprintf( f, " \"%s\", \"%s\", %08x, {\n", block->name,
1.241 + block->description == NULL ? block->name : block->description,
1.242 + block->address );
1.243 + fprintf( f, " mmio_region_%s_read, mmio_region_%s_write,\n", block->name, block->name );
1.244 + fprintf( f, " mmio_region_%s_read_word, mmio_region_%s_write_word,\n", block->name, block->name );
1.245 + fprintf( f, " mmio_region_%s_read_byte, mmio_region_%s_write_byte,\n", block->name, block->name );
1.246 + fprintf( f, " mmio_region_%s_read_burst, mmio_region_%s_write_burst,\n", block->name, block->name );
1.247 + fprintf( f, " unmapped_prefetch, mmio_region_%s_read_byte },\n", block->name, block->name );
1.248 + fprintf( f, " NULL, NULL, {\n" );
1.249 + for( unsigned i=0; i<block->numRegs; i++ ) {
1.250 + regdef_t reg = block->regs[i];
1.251 + if( reg->mode != REG_CONST ) {
1.252 + char tmp[32];
1.253 + const char *regname = reg->name;
1.254 + if( regname == NULL ) {
1.255 + regname = tmp;
1.256 + snprintf( tmp, sizeof(tmp), "%s_%03X", block->name, reg->offset );
1.257 + }
1.258 +
1.259 + fprintf( f, " { \"%s\", \"%s\", %d, 0x%03x, 0x%08x, %s },\n", regname,
1.260 + reg->description == NULL ? regname : reg->description,
1.261 + reg->numBytes*8, reg->offset, (unsigned)reg->initValue.i,
1.262 + (reg->mode == REG_RW ? "PORT_MRW" : (reg->mode == REG_WO ? "PORT_W" : "PORT_R")) );
1.263 + }
1.264 + }
1.265 + fprintf( f, " }, NULL };\n" );
1.266 + }
1.267 +
1.268 +}
1.269 +
1.270 +void write_header( FILE *f, GList *blocks )
1.271 +{
1.272 + fputs( file_top, f );
1.273 +
1.274 + fputs( "\n#include \"mmio.h\"\n\n", f );
1.275 +
1.276 + for( GList *ptr = blocks; ptr != NULL; ptr = ptr->next ) {
1.277 + regblock_t block = (regblock_t)ptr->data;
1.278 + fprintf( f, "extern struct mmio_region mmio_region_%s;\n", block->name );
1.279 + fprintf( f, "enum mmio_region_%s_port_t {\n", block->name );
1.280 + for( unsigned i=0; i<block->numRegs; i++ ) {
1.281 + regdef_t reg = block->regs[i];
1.282 + if( reg->name != NULL ) {
1.283 + fprintf( f, " %s = 0x%03x,\n", reg->name, reg->offset );
1.284 + }
1.285 + }
1.286 + fprintf( f, "};\n" );
1.287 + }
1.288 +}
1.289 +
1.290 +void write_test( FILE *f, GList *blocks )
1.291 +{
1.292 +
1.293 +
1.294 +}
1.295 +
1.296 +int main(int argc, char *argv[])
1.297 +{
1.298 + const char *header = NULL;
1.299 + const char *output = NULL;
1.300 + gboolean check_only = FALSE;
1.301 + int verbose = 0, opt;
1.302 + GList *block_list = NULL;
1.303 +
1.304 + while( (opt = getopt_long( argc, argv, short_options, long_options, NULL )) != -1 ) {
1.305 + switch(opt) {
1.306 + case 'c':
1.307 + check_only = TRUE;
1.308 + break;
1.309 + case 'd':
1.310 + header = optarg;
1.311 + break;
1.312 + case 'h':
1.313 + print_usage();
1.314 + exit(0);
1.315 + case 'o':
1.316 + output = optarg;
1.317 + break;
1.318 + case 'v':
1.319 + verbose++;
1.320 + break;
1.321 + }
1.322 + }
1.323 +
1.324 + if( optind == argc ) {
1.325 + print_usage();
1.326 + exit(1);
1.327 + }
1.328 +
1.329 + if( output == NULL ) {
1.330 + output = makeFilename(argv[optind],".c");
1.331 + }
1.332 + if( header == NULL ) {
1.333 + header = makeFilename(argv[optind],".h");
1.334 + }
1.335 +
1.336 + for( ; optind < argc; optind++ ) {
1.337 + block_list = ioparse(argv[optind], block_list);
1.338 + }
1.339 +
1.340 + if( verbose ) {
1.341 + dump_register_blocks(stdout, block_list, verbose>1);
1.342 + }
1.343 +
1.344 + int errors = check_registers(block_list);
1.345 + if( errors != 0 ) {
1.346 + fprintf( stderr, "Aborting due to validation errors\n" );
1.347 + return 2;
1.348 + }
1.349 +
1.350 + if( !check_only ) {
1.351 + FILE *f = fopen( output, "wo" );
1.352 + if( f == NULL ) {
1.353 + fprintf( stderr, "Unable to open output file '%s': %s\n", output, strerror(errno) );
1.354 + return 3;
1.355 + }
1.356 + write_source( f, block_list, header );
1.357 + fclose(f);
1.358 +
1.359 + f = fopen( header, "wo" );
1.360 + if( f == NULL ) {
1.361 + fprintf( stderr, "Unable to open header file '%s': %s\n", header, strerror(errno) );
1.362 + return 4;
1.363 + }
1.364 + write_header( f, block_list );
1.365 + fclose(f);
1.366 + }
1.367 +
1.368 + for( GList *ptr = block_list; ptr != NULL; ptr = ptr->next ) {
1.369 + char *data = build_page_initializer((regblock_t)ptr->data);
1.370 + fwrite_dump( data, LXDREAM_PAGE_SIZE, stdout );
1.371 + g_free(data);
1.372 + }
1.373 + return 0;
1.374 +}
1.375 +
.