filename | src/tools/genmach.c |
changeset | 1104:700e16c321e5 |
next | 1296:30ecee61f811 |
author | nkeynes |
date | Sun Feb 12 16:30:26 2012 +1000 (11 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 |
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +00001.2 +++ b/src/tools/genmach.c Sun Feb 12 16:30:26 2012 +10001.3 @@ -0,0 +1,372 @@1.4 +/**1.5 + * $Id$1.6 + *1.7 + * mmio register code generator1.8 + *1.9 + * Copyright (c) 2010 Nathan Keynes.1.10 + *1.11 + * This program is free software; you can redistribute it and/or modify1.12 + * it under the terms of the GNU General Public License as published by1.13 + * the Free Software Foundation; either version 2 of the License, or1.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 of1.18 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the1.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 40961.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", return1.64 + * a newly allocated string "file<ext>" where <ext> is the second1.65 + * parameter.1.66 + *1.67 + * @param baseName a non-null filename1.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 operation1.86 + * is well-defined.1.87 + * 2. Replaces transitive mirror groups with a set of equivalent direct1.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 and1.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 there1.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 + else1.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 +
.