4 * mmio register code generator
6 * Copyright (c) 2010 Nathan Keynes.
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
26 #include <glib/gstrfuncs.h>
27 #include <glib/glist.h>
31 #define LXDREAM_PAGE_SIZE 4096
33 const char *short_options = "cd:ho:v";
34 struct option long_options[] = {
35 { "output", required_argument, NULL, 'o' },
36 { "header", required_argument, NULL, 'd' },
37 { "check-only", no_argument, NULL, 'c' },
38 { "help", no_argument, NULL, 'h' },
39 { "verbose", no_argument, NULL, 'v' },
42 static void print_version()
44 printf( "genmmio 0.1\n" );
47 static void print_usage()
50 printf( "Usage: genmmio [options] <input-register-files>\n" );
51 printf( "Options:\n" );
52 printf( " -c, --check-only %s\n", _("Check specification files but don't write any output") );
53 printf( " -d, --header=FILE %s\n", _("Specify header output file [corresponding .h for .c file]") );
54 printf( " -h, --help %s\n", _("Display this usage information") );
55 printf( " -o, --output=FILE %s\n", _("Specify main output file [corresponding .c for input file]") );
56 printf( " -v, --verbose %s\n", _("Print verbose output") );
60 * Given an input baseName of the form "path/file.blah", return
61 * a newly allocated string "file<ext>" where <ext> is the second
64 * @param baseName a non-null filename
65 * @param ext a file extension including leading '.'
67 char *makeFilename( const char *baseName, const char *ext )
69 const char *p = strrchr(baseName, '/');
72 const char *q = strchr(p, '.');
74 return g_strdup_printf("%s%s", p, ext);
76 return g_strdup_printf("%.*s%s", q-p, p, ext );
81 * Process all registers after parsing is complete. This does a few things:
82 * 1. Ensures that there are no overlapping registers, so that every operation
84 * 2. Replaces transitive mirror groups with a set of equivalent direct
85 * mirrors so that later processing doesn't need to check for this case.
86 * This also checks for cycles in the graph.
87 * 3. Organises the memory map(s) into page-size quantities (combining and
88 * splitting groups where necessary).
90 GList *postprocess_registers( GList *blocks )
92 for( GList *ptr = blocks; ptr != NULL; ptr = ptr->next ) {
93 regblock_t block = (regblock_t)ptr->data;
94 for( unsigned i=0; i<block->numRegs; i++ ) {
95 regdef_t reg = block->regs[i];
96 if( reg->mode == REG_MIRROR ) {
104 * Check register definition semantics. Primarily this verifies that there
105 * are no overlapping definitions.
106 * @return number of errors found.
108 int check_registers( GList *registers )
114 * Dump the high-level register map to the given file.
116 void dump_register_blocks( FILE *f, GList *blocks, gboolean detail )
118 const char *mode_names[] = { "--", "RW", "RO", "RW" };
119 for( GList *ptr = blocks; ptr != NULL; ptr=ptr->next ) {
120 regblock_t block = (regblock_t)ptr->data;
121 fprintf( f, "%08X: %s - %s\n", block->address,
123 (block->description == NULL ? "<no description>" : block->description) );
125 for( unsigned i=0; i<block->numRegs; i++ ) {
126 regdef_t reg = block->regs[i];
127 fprintf( f, " %04X: %s %s", reg->offset,
128 mode_names[reg->mode],
129 reg->name == NULL ? "<anon>" : reg->name );
130 if( reg->numElements > 1 ) {
131 fprintf( f, "[%d]", reg->numElements );
133 fprintf( f, " - %s\n",
134 (reg->description == NULL ? "<no description>" : reg->description) );
140 char *build_page_initializer( regblock_t block )
142 char *page = g_malloc(LXDREAM_PAGE_SIZE);
144 /* First, background fill if any */
145 if( block->flags.fillSizeBytes == 0 ) {
146 memset(page, 0, LXDREAM_PAGE_SIZE);
148 for( unsigned i=0; i<LXDREAM_PAGE_SIZE; i++ ) {
149 page[i] = block->flags.fillValue.a[i%block->flags.fillSizeBytes];
153 /* Next, set register initializer (except for wo + mirror regs) */
154 for( unsigned i=0; i<block->numRegs; i++ ) {
155 regdef_t reg = block->regs[i];
156 if( reg->mode != REG_WO && reg->mode != REG_MIRROR ) {
157 unsigned offset = reg->offset;
158 for( unsigned j=0; j<reg->numElements; j++ ) {
159 if( reg->type == REG_STRING ) {
160 memcpy( &page[offset], reg->initValue.s, reg->numBytes );
162 memcpy( &page[offset], reg->initValue.a, reg->numBytes );
164 offset += reg->numBytes;
169 /* Finally clone mirror groups */
170 for( unsigned i=0; i<block->numRegs; i++ ) {
171 regdef_t reg = block->regs[i];
172 if( reg->mode == REG_MIRROR ) {
173 unsigned offset = reg->offset;
174 unsigned target = reg->initValue.i;
175 for( unsigned i=0; i<reg->numElements; i++ ) {
176 memcpy( &page[offset], &page[target], reg->numBytes );
177 offset += reg->stride;
186 void fwrite_dump( unsigned char *data, unsigned int length, FILE *f )
190 for( i =0; i<length; i+=16 ) {
191 if( i >= 16 && i < length-16 && memcmp( &data[i], &data[i-16], 16) == 0 ) {
196 fprintf( f, " ** \n" );
199 fprintf( f, "%08X:", i);
200 for( j=i; j<i+16; j++ ) {
201 if( j != i && (j % 4) == 0 )
204 fprintf( f, " %02X", (unsigned int)(data[j]) );
209 for( j=i; j<i+16 && j<length; j++ ) {
210 fprintf( f, "%c", isprint(data[j]) ? data[j] : '.' );
216 static const char *file_top = "/**\n"
217 " * This file was automatically generated by genmmio, do not edit\n"
220 void write_source( FILE *f, GList *blocks, const char *header_filename )
222 fputs( file_top, f );
223 fprintf( f, "\n#include \"%s\"\n\n", header_filename );
225 for( GList *ptr = blocks; ptr != NULL; ptr = ptr->next ) {
226 regblock_t block = (regblock_t)ptr->data;
227 /* Generate the mmio region struct */
228 fprintf( f, "void FASTCALL mmio_region_%s_write(uint32_t, uint32_t);\n", block->name );
229 fprintf( f, "void FASTCALL mmio_region_%s_write_word(uint32_t, uint32_t);\n", block->name );
230 fprintf( f, "void FASTCALL mmio_region_%s_write_byte(uint32_t, uint32_t);\n", block->name );
231 fprintf( f, "void FASTCALL mmio_region_%s_write_burst(uint32_t, unsigned char *);\n", block->name );
232 fprintf( f, "int32_t FASTCALL mmio_region_%s_read(uint32_t);\n", block->name );
233 fprintf( f, "int32_t FASTCALL mmio_region_%s_read_word(uint32_t);\n", block->name );
234 fprintf( f, "int32_t FASTCALL mmio_region_%s_read_byte(uint32_t);\n", block->name );
235 fprintf( f, "void FASTCALL mmio_region_%s_read_burst(unsigned char *, uint32_t);\n", block->name );
236 fprintf( f, "struct mmio_region mmio_region_%s = {\n", block->name );
237 fprintf( f, " \"%s\", \"%s\", %08x, {\n", block->name,
238 block->description == NULL ? block->name : block->description,
240 fprintf( f, " mmio_region_%s_read, mmio_region_%s_write,\n", block->name, block->name );
241 fprintf( f, " mmio_region_%s_read_word, mmio_region_%s_write_word,\n", block->name, block->name );
242 fprintf( f, " mmio_region_%s_read_byte, mmio_region_%s_write_byte,\n", block->name, block->name );
243 fprintf( f, " mmio_region_%s_read_burst, mmio_region_%s_write_burst,\n", block->name, block->name );
244 fprintf( f, " unmapped_prefetch, mmio_region_%s_read_byte },\n", block->name, block->name );
245 fprintf( f, " NULL, NULL, {\n" );
246 for( unsigned i=0; i<block->numRegs; i++ ) {
247 regdef_t reg = block->regs[i];
248 if( reg->mode != REG_CONST ) {
250 const char *regname = reg->name;
251 if( regname == NULL ) {
253 snprintf( tmp, sizeof(tmp), "%s_%03X", block->name, reg->offset );
256 fprintf( f, " { \"%s\", \"%s\", %d, 0x%03x, 0x%08x, %s },\n", regname,
257 reg->description == NULL ? regname : reg->description,
258 reg->numBytes*8, reg->offset, (unsigned)reg->initValue.i,
259 (reg->mode == REG_RW ? "PORT_MRW" : (reg->mode == REG_WO ? "PORT_W" : "PORT_R")) );
262 fprintf( f, " }, NULL };\n" );
267 void write_header( FILE *f, GList *blocks )
269 fputs( file_top, f );
271 fputs( "\n#include \"mmio.h\"\n\n", f );
273 for( GList *ptr = blocks; ptr != NULL; ptr = ptr->next ) {
274 regblock_t block = (regblock_t)ptr->data;
275 fprintf( f, "extern struct mmio_region mmio_region_%s;\n", block->name );
276 fprintf( f, "enum mmio_region_%s_port_t {\n", block->name );
277 for( unsigned i=0; i<block->numRegs; i++ ) {
278 regdef_t reg = block->regs[i];
279 if( reg->name != NULL ) {
280 fprintf( f, " %s = 0x%03x,\n", reg->name, reg->offset );
283 fprintf( f, "};\n" );
287 void write_test( FILE *f, GList *blocks )
293 int main(int argc, char *argv[])
295 const char *header = NULL;
296 const char *output = NULL;
297 gboolean check_only = FALSE;
298 int verbose = 0, opt;
299 GList *block_list = NULL;
301 while( (opt = getopt_long( argc, argv, short_options, long_options, NULL )) != -1 ) {
321 if( optind == argc ) {
326 if( output == NULL ) {
327 output = makeFilename(argv[optind],".c");
329 if( header == NULL ) {
330 header = makeFilename(argv[optind],".h");
333 for( ; optind < argc; optind++ ) {
334 block_list = ioparse(argv[optind], block_list);
338 dump_register_blocks(stdout, block_list, verbose>1);
341 int errors = check_registers(block_list);
343 fprintf( stderr, "Aborting due to validation errors\n" );
348 FILE *f = fopen( output, "wo" );
350 fprintf( stderr, "Unable to open output file '%s': %s\n", output, strerror(errno) );
353 write_source( f, block_list, header );
356 f = fopen( header, "wo" );
358 fprintf( stderr, "Unable to open header file '%s': %s\n", header, strerror(errno) );
361 write_header( f, block_list );
365 for( GList *ptr = block_list; ptr != NULL; ptr = ptr->next ) {
366 char *data = build_page_initializer((regblock_t)ptr->data);
367 fwrite_dump( data, LXDREAM_PAGE_SIZE, stdout );
.