nkeynes@1104: /** nkeynes@1104: * $Id$ nkeynes@1104: * nkeynes@1104: * mmio register code generator nkeynes@1104: * nkeynes@1104: * Copyright (c) 2010 Nathan Keynes. nkeynes@1104: * nkeynes@1104: * This program is free software; you can redistribute it and/or modify nkeynes@1104: * it under the terms of the GNU General Public License as published by nkeynes@1104: * the Free Software Foundation; either version 2 of the License, or nkeynes@1104: * (at your option) any later version. nkeynes@1104: * nkeynes@1104: * This program is distributed in the hope that it will be useful, nkeynes@1104: * but WITHOUT ANY WARRANTY; without even the implied warranty of nkeynes@1104: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the nkeynes@1104: * GNU General Public License for more details. nkeynes@1104: */ nkeynes@1104: nkeynes@1104: #include nkeynes@1104: #include nkeynes@1104: #include nkeynes@1104: #include nkeynes@1104: #include nkeynes@1104: #include nkeynes@1104: #include nkeynes@1104: #include nkeynes@1104: #include nkeynes@1104: #include "gettext.h" nkeynes@1104: #include "genmach.h" nkeynes@1104: nkeynes@1104: #define LXDREAM_PAGE_SIZE 4096 nkeynes@1104: nkeynes@1104: const char *short_options = "cd:ho:v"; nkeynes@1104: struct option long_options[] = { nkeynes@1104: { "output", required_argument, NULL, 'o' }, nkeynes@1104: { "header", required_argument, NULL, 'd' }, nkeynes@1104: { "check-only", no_argument, NULL, 'c' }, nkeynes@1104: { "help", no_argument, NULL, 'h' }, nkeynes@1104: { "verbose", no_argument, NULL, 'v' }, nkeynes@1104: { NULL, 0, 0, 0 } }; nkeynes@1104: nkeynes@1104: static void print_version() nkeynes@1104: { nkeynes@1104: printf( "genmmio 0.1\n" ); nkeynes@1104: } nkeynes@1104: nkeynes@1104: static void print_usage() nkeynes@1104: { nkeynes@1104: print_version(); nkeynes@1104: printf( "Usage: genmmio [options] \n" ); nkeynes@1104: printf( "Options:\n" ); nkeynes@1104: printf( " -c, --check-only %s\n", _("Check specification files but don't write any output") ); nkeynes@1104: printf( " -d, --header=FILE %s\n", _("Specify header output file [corresponding .h for .c file]") ); nkeynes@1104: printf( " -h, --help %s\n", _("Display this usage information") ); nkeynes@1104: printf( " -o, --output=FILE %s\n", _("Specify main output file [corresponding .c for input file]") ); nkeynes@1104: printf( " -v, --verbose %s\n", _("Print verbose output") ); nkeynes@1104: } nkeynes@1104: nkeynes@1104: /** nkeynes@1104: * Given an input baseName of the form "path/file.blah", return nkeynes@1104: * a newly allocated string "file" where is the second nkeynes@1104: * parameter. nkeynes@1104: * nkeynes@1104: * @param baseName a non-null filename nkeynes@1104: * @param ext a file extension including leading '.' nkeynes@1104: */ nkeynes@1104: char *makeFilename( const char *baseName, const char *ext ) nkeynes@1104: { nkeynes@1104: const char *p = strrchr(baseName, '/'); nkeynes@1104: if( p == NULL ) nkeynes@1104: p = baseName; nkeynes@1104: const char *q = strchr(p, '.'); nkeynes@1104: if( q == NULL ) { nkeynes@1104: return g_strdup_printf("%s%s", p, ext); nkeynes@1104: } else { nkeynes@1104: return g_strdup_printf("%.*s%s", q-p, p, ext ); nkeynes@1104: } nkeynes@1104: } nkeynes@1104: nkeynes@1104: /** nkeynes@1104: * Process all registers after parsing is complete. This does a few things: nkeynes@1104: * 1. Ensures that there are no overlapping registers, so that every operation nkeynes@1104: * is well-defined. nkeynes@1104: * 2. Replaces transitive mirror groups with a set of equivalent direct nkeynes@1104: * mirrors so that later processing doesn't need to check for this case. nkeynes@1104: * This also checks for cycles in the graph. nkeynes@1104: * 3. Organises the memory map(s) into page-size quantities (combining and nkeynes@1104: * splitting groups where necessary). nkeynes@1104: */ nkeynes@1104: GList *postprocess_registers( GList *blocks ) nkeynes@1104: { nkeynes@1104: for( GList *ptr = blocks; ptr != NULL; ptr = ptr->next ) { nkeynes@1104: regblock_t block = (regblock_t)ptr->data; nkeynes@1104: for( unsigned i=0; inumRegs; i++ ) { nkeynes@1104: regdef_t reg = block->regs[i]; nkeynes@1104: if( reg->mode == REG_MIRROR ) { nkeynes@1104: } nkeynes@1104: } nkeynes@1104: } nkeynes@1104: return blocks; nkeynes@1104: } nkeynes@1104: nkeynes@1104: /** nkeynes@1104: * Check register definition semantics. Primarily this verifies that there nkeynes@1104: * are no overlapping definitions. nkeynes@1104: * @return number of errors found. nkeynes@1104: */ nkeynes@1104: int check_registers( GList *registers ) nkeynes@1104: { nkeynes@1104: return 0; nkeynes@1104: } nkeynes@1104: nkeynes@1104: /** nkeynes@1104: * Dump the high-level register map to the given file. nkeynes@1104: */ nkeynes@1104: void dump_register_blocks( FILE *f, GList *blocks, gboolean detail ) nkeynes@1104: { nkeynes@1104: const char *mode_names[] = { "--", "RW", "RO", "RW" }; nkeynes@1104: for( GList *ptr = blocks; ptr != NULL; ptr=ptr->next ) { nkeynes@1104: regblock_t block = (regblock_t)ptr->data; nkeynes@1104: fprintf( f, "%08X: %s - %s\n", block->address, nkeynes@1104: block->name, nkeynes@1104: (block->description == NULL ? "" : block->description) ); nkeynes@1104: if( detail ) { nkeynes@1104: for( unsigned i=0; inumRegs; i++ ) { nkeynes@1104: regdef_t reg = block->regs[i]; nkeynes@1104: fprintf( f, " %04X: %s %s", reg->offset, nkeynes@1104: mode_names[reg->mode], nkeynes@1104: reg->name == NULL ? "" : reg->name ); nkeynes@1104: if( reg->numElements > 1 ) { nkeynes@1104: fprintf( f, "[%d]", reg->numElements ); nkeynes@1104: } nkeynes@1104: fprintf( f, " - %s\n", nkeynes@1104: (reg->description == NULL ? "" : reg->description) ); nkeynes@1104: } nkeynes@1104: } nkeynes@1104: } nkeynes@1104: } nkeynes@1104: nkeynes@1104: char *build_page_initializer( regblock_t block ) nkeynes@1104: { nkeynes@1104: char *page = g_malloc(LXDREAM_PAGE_SIZE); nkeynes@1104: nkeynes@1104: /* First, background fill if any */ nkeynes@1104: if( block->flags.fillSizeBytes == 0 ) { nkeynes@1104: memset(page, 0, LXDREAM_PAGE_SIZE); nkeynes@1104: } else { nkeynes@1104: for( unsigned i=0; iflags.fillValue.a[i%block->flags.fillSizeBytes]; nkeynes@1104: } nkeynes@1104: } nkeynes@1104: nkeynes@1104: /* Next, set register initializer (except for wo + mirror regs) */ nkeynes@1104: for( unsigned i=0; inumRegs; i++ ) { nkeynes@1104: regdef_t reg = block->regs[i]; nkeynes@1104: if( reg->mode != REG_WO && reg->mode != REG_MIRROR ) { nkeynes@1104: unsigned offset = reg->offset; nkeynes@1104: for( unsigned j=0; jnumElements; j++ ) { nkeynes@1104: if( reg->type == REG_STRING ) { nkeynes@1104: memcpy( &page[offset], reg->initValue.s, reg->numBytes ); nkeynes@1104: } else { nkeynes@1104: memcpy( &page[offset], reg->initValue.a, reg->numBytes ); nkeynes@1104: } nkeynes@1104: offset += reg->numBytes; nkeynes@1104: } nkeynes@1104: } nkeynes@1104: } nkeynes@1104: nkeynes@1104: /* Finally clone mirror groups */ nkeynes@1104: for( unsigned i=0; inumRegs; i++ ) { nkeynes@1104: regdef_t reg = block->regs[i]; nkeynes@1104: if( reg->mode == REG_MIRROR ) { nkeynes@1104: unsigned offset = reg->offset; nkeynes@1104: unsigned target = reg->initValue.i; nkeynes@1104: for( unsigned i=0; inumElements; i++ ) { nkeynes@1104: memcpy( &page[offset], &page[target], reg->numBytes ); nkeynes@1104: offset += reg->stride; nkeynes@1104: } nkeynes@1104: } nkeynes@1104: } nkeynes@1104: nkeynes@1104: return page; nkeynes@1104: } nkeynes@1104: nkeynes@1104: nkeynes@1104: void fwrite_dump( unsigned char *data, unsigned int length, FILE *f ) nkeynes@1104: { nkeynes@1104: unsigned int i, j; nkeynes@1104: int skipped = 0; nkeynes@1104: for( i =0; i= 16 && i < length-16 && memcmp( &data[i], &data[i-16], 16) == 0 ) { nkeynes@1104: skipped++; nkeynes@1104: continue; nkeynes@1104: } nkeynes@1104: if( skipped ) { nkeynes@1104: fprintf( f, " ** \n" ); nkeynes@1104: skipped = 0; nkeynes@1104: } nkeynes@1104: fprintf( f, "%08X:", i); nkeynes@1104: for( j=i; jnext ) { nkeynes@1104: regblock_t block = (regblock_t)ptr->data; nkeynes@1104: /* Generate the mmio region struct */ nkeynes@1104: fprintf( f, "void FASTCALL mmio_region_%s_write(uint32_t, uint32_t);\n", block->name ); nkeynes@1104: fprintf( f, "void FASTCALL mmio_region_%s_write_word(uint32_t, uint32_t);\n", block->name ); nkeynes@1104: fprintf( f, "void FASTCALL mmio_region_%s_write_byte(uint32_t, uint32_t);\n", block->name ); nkeynes@1104: fprintf( f, "void FASTCALL mmio_region_%s_write_burst(uint32_t, unsigned char *);\n", block->name ); nkeynes@1104: fprintf( f, "int32_t FASTCALL mmio_region_%s_read(uint32_t);\n", block->name ); nkeynes@1104: fprintf( f, "int32_t FASTCALL mmio_region_%s_read_word(uint32_t);\n", block->name ); nkeynes@1104: fprintf( f, "int32_t FASTCALL mmio_region_%s_read_byte(uint32_t);\n", block->name ); nkeynes@1104: fprintf( f, "void FASTCALL mmio_region_%s_read_burst(unsigned char *, uint32_t);\n", block->name ); nkeynes@1104: fprintf( f, "struct mmio_region mmio_region_%s = {\n", block->name ); nkeynes@1104: fprintf( f, " \"%s\", \"%s\", %08x, {\n", block->name, nkeynes@1104: block->description == NULL ? block->name : block->description, nkeynes@1104: block->address ); nkeynes@1104: fprintf( f, " mmio_region_%s_read, mmio_region_%s_write,\n", block->name, block->name ); nkeynes@1104: fprintf( f, " mmio_region_%s_read_word, mmio_region_%s_write_word,\n", block->name, block->name ); nkeynes@1104: fprintf( f, " mmio_region_%s_read_byte, mmio_region_%s_write_byte,\n", block->name, block->name ); nkeynes@1104: fprintf( f, " mmio_region_%s_read_burst, mmio_region_%s_write_burst,\n", block->name, block->name ); nkeynes@1104: fprintf( f, " unmapped_prefetch, mmio_region_%s_read_byte },\n", block->name, block->name ); nkeynes@1104: fprintf( f, " NULL, NULL, {\n" ); nkeynes@1104: for( unsigned i=0; inumRegs; i++ ) { nkeynes@1104: regdef_t reg = block->regs[i]; nkeynes@1104: if( reg->mode != REG_CONST ) { nkeynes@1104: char tmp[32]; nkeynes@1104: const char *regname = reg->name; nkeynes@1104: if( regname == NULL ) { nkeynes@1104: regname = tmp; nkeynes@1104: snprintf( tmp, sizeof(tmp), "%s_%03X", block->name, reg->offset ); nkeynes@1104: } nkeynes@1104: nkeynes@1104: fprintf( f, " { \"%s\", \"%s\", %d, 0x%03x, 0x%08x, %s },\n", regname, nkeynes@1104: reg->description == NULL ? regname : reg->description, nkeynes@1104: reg->numBytes*8, reg->offset, (unsigned)reg->initValue.i, nkeynes@1104: (reg->mode == REG_RW ? "PORT_MRW" : (reg->mode == REG_WO ? "PORT_W" : "PORT_R")) ); nkeynes@1104: } nkeynes@1104: } nkeynes@1104: fprintf( f, " }, NULL };\n" ); nkeynes@1104: } nkeynes@1104: nkeynes@1104: } nkeynes@1104: nkeynes@1104: void write_header( FILE *f, GList *blocks ) nkeynes@1104: { nkeynes@1104: fputs( file_top, f ); nkeynes@1104: nkeynes@1104: fputs( "\n#include \"mmio.h\"\n\n", f ); nkeynes@1104: nkeynes@1104: for( GList *ptr = blocks; ptr != NULL; ptr = ptr->next ) { nkeynes@1104: regblock_t block = (regblock_t)ptr->data; nkeynes@1104: fprintf( f, "extern struct mmio_region mmio_region_%s;\n", block->name ); nkeynes@1104: fprintf( f, "enum mmio_region_%s_port_t {\n", block->name ); nkeynes@1104: for( unsigned i=0; inumRegs; i++ ) { nkeynes@1104: regdef_t reg = block->regs[i]; nkeynes@1104: if( reg->name != NULL ) { nkeynes@1104: fprintf( f, " %s = 0x%03x,\n", reg->name, reg->offset ); nkeynes@1104: } nkeynes@1104: } nkeynes@1104: fprintf( f, "};\n" ); nkeynes@1104: } nkeynes@1104: } nkeynes@1104: nkeynes@1104: void write_test( FILE *f, GList *blocks ) nkeynes@1104: { nkeynes@1104: nkeynes@1104: nkeynes@1104: } nkeynes@1104: nkeynes@1104: int main(int argc, char *argv[]) nkeynes@1104: { nkeynes@1104: const char *header = NULL; nkeynes@1104: const char *output = NULL; nkeynes@1104: gboolean check_only = FALSE; nkeynes@1104: int verbose = 0, opt; nkeynes@1104: GList *block_list = NULL; nkeynes@1104: nkeynes@1104: while( (opt = getopt_long( argc, argv, short_options, long_options, NULL )) != -1 ) { nkeynes@1104: switch(opt) { nkeynes@1104: case 'c': nkeynes@1104: check_only = TRUE; nkeynes@1104: break; nkeynes@1104: case 'd': nkeynes@1104: header = optarg; nkeynes@1104: break; nkeynes@1104: case 'h': nkeynes@1104: print_usage(); nkeynes@1104: exit(0); nkeynes@1104: case 'o': nkeynes@1104: output = optarg; nkeynes@1104: break; nkeynes@1104: case 'v': nkeynes@1104: verbose++; nkeynes@1104: break; nkeynes@1104: } nkeynes@1104: } nkeynes@1104: nkeynes@1104: if( optind == argc ) { nkeynes@1104: print_usage(); nkeynes@1104: exit(1); nkeynes@1104: } nkeynes@1104: nkeynes@1104: if( output == NULL ) { nkeynes@1104: output = makeFilename(argv[optind],".c"); nkeynes@1104: } nkeynes@1104: if( header == NULL ) { nkeynes@1104: header = makeFilename(argv[optind],".h"); nkeynes@1104: } nkeynes@1104: nkeynes@1104: for( ; optind < argc; optind++ ) { nkeynes@1104: block_list = ioparse(argv[optind], block_list); nkeynes@1104: } nkeynes@1104: nkeynes@1104: if( verbose ) { nkeynes@1104: dump_register_blocks(stdout, block_list, verbose>1); nkeynes@1104: } nkeynes@1104: nkeynes@1104: int errors = check_registers(block_list); nkeynes@1104: if( errors != 0 ) { nkeynes@1104: fprintf( stderr, "Aborting due to validation errors\n" ); nkeynes@1104: return 2; nkeynes@1104: } nkeynes@1104: nkeynes@1104: if( !check_only ) { nkeynes@1104: FILE *f = fopen( output, "wo" ); nkeynes@1104: if( f == NULL ) { nkeynes@1104: fprintf( stderr, "Unable to open output file '%s': %s\n", output, strerror(errno) ); nkeynes@1104: return 3; nkeynes@1104: } nkeynes@1104: write_source( f, block_list, header ); nkeynes@1104: fclose(f); nkeynes@1104: nkeynes@1104: f = fopen( header, "wo" ); nkeynes@1104: if( f == NULL ) { nkeynes@1104: fprintf( stderr, "Unable to open header file '%s': %s\n", header, strerror(errno) ); nkeynes@1104: return 4; nkeynes@1104: } nkeynes@1104: write_header( f, block_list ); nkeynes@1104: fclose(f); nkeynes@1104: } nkeynes@1104: nkeynes@1104: for( GList *ptr = block_list; ptr != NULL; ptr = ptr->next ) { nkeynes@1104: char *data = build_page_initializer((regblock_t)ptr->data); nkeynes@1104: fwrite_dump( data, LXDREAM_PAGE_SIZE, stdout ); nkeynes@1104: g_free(data); nkeynes@1104: } nkeynes@1104: return 0; nkeynes@1104: } nkeynes@1104: