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.
30 #define LXDREAM_PAGE_SIZE 4096
32 const char *short_options = "cd:ho:v";
33 struct option long_options[] = {
34 { "output", required_argument, NULL, 'o' },
35 { "header", required_argument, NULL, 'd' },
36 { "check-only", no_argument, NULL, 'c' },
37 { "help", no_argument, NULL, 'h' },
38 { "verbose", no_argument, NULL, 'v' },
41 static void print_version()
43 printf( "genmmio 0.1\n" );
46 static void print_usage()
49 printf( "Usage: genmmio [options] <input-register-files>\n" );
50 printf( "Options:\n" );
51 printf( " -c, --check-only %s\n", _("Check specification files but don't write any output") );
52 printf( " -d, --header=FILE %s\n", _("Specify header output file [corresponding .h for .c file]") );
53 printf( " -h, --help %s\n", _("Display this usage information") );
54 printf( " -o, --output=FILE %s\n", _("Specify main output file [corresponding .c for input file]") );
55 printf( " -v, --verbose %s\n", _("Print verbose output") );
59 * Given an input baseName of the form "path/file.blah", return
60 * a newly allocated string "file<ext>" where <ext> is the second
63 * @param baseName a non-null filename
64 * @param ext a file extension including leading '.'
66 char *makeFilename( const char *baseName, const char *ext )
68 const char *p = strrchr(baseName, '/');
71 const char *q = strchr(p, '.');
73 return g_strdup_printf("%s%s", p, ext);
75 return g_strdup_printf("%.*s%s", (int)(q-p), p, ext );
80 * Process all registers after parsing is complete. This does a few things:
81 * 1. Ensures that there are no overlapping registers, so that every operation
83 * 2. Replaces transitive mirror groups with a set of equivalent direct
84 * mirrors so that later processing doesn't need to check for this case.
85 * This also checks for cycles in the graph.
86 * 3. Organises the memory map(s) into page-size quantities (combining and
87 * splitting groups where necessary).
89 GList *postprocess_registers( GList *blocks )
91 for( GList *ptr = blocks; ptr != NULL; ptr = ptr->next ) {
92 regblock_t block = (regblock_t)ptr->data;
93 for( unsigned i=0; i<block->numRegs; i++ ) {
94 regdef_t reg = block->regs[i];
95 if( reg->mode == REG_MIRROR ) {
103 * Check register definition semantics. Primarily this verifies that there
104 * are no overlapping definitions.
105 * @return number of errors found.
107 int check_registers( GList *registers )
113 * Dump the high-level register map to the given file.
115 void dump_register_blocks( FILE *f, GList *blocks, gboolean detail )
117 const char *mode_names[] = { "--", "RW", "RO", "RW" };
118 for( GList *ptr = blocks; ptr != NULL; ptr=ptr->next ) {
119 regblock_t block = (regblock_t)ptr->data;
120 fprintf( f, "%08X: %s - %s\n", block->address,
122 (block->description == NULL ? "<no description>" : block->description) );
124 for( unsigned i=0; i<block->numRegs; i++ ) {
125 regdef_t reg = block->regs[i];
126 fprintf( f, " %04X: %s %s", reg->offset,
127 mode_names[reg->mode],
128 reg->name == NULL ? "<anon>" : reg->name );
129 if( reg->numElements > 1 ) {
130 fprintf( f, "[%d]", reg->numElements );
132 fprintf( f, " - %s\n",
133 (reg->description == NULL ? "<no description>" : reg->description) );
139 unsigned char *build_page_initializer( regblock_t block )
141 unsigned char *page = g_malloc(LXDREAM_PAGE_SIZE);
143 /* First, background fill if any */
144 if( block->flags.fillSizeBytes == 0 ) {
145 memset(page, 0, LXDREAM_PAGE_SIZE);
147 for( unsigned i=0; i<LXDREAM_PAGE_SIZE; i++ ) {
148 page[i] = block->flags.fillValue.a[i%block->flags.fillSizeBytes];
152 /* Next, set register initializer (except for wo + mirror regs) */
153 for( unsigned i=0; i<block->numRegs; i++ ) {
154 regdef_t reg = block->regs[i];
155 if( reg->mode != REG_WO && reg->mode != REG_MIRROR ) {
156 unsigned offset = reg->offset;
157 for( unsigned j=0; j<reg->numElements; j++ ) {
158 if( reg->type == REG_STRING ) {
159 memcpy( &page[offset], reg->initValue.s, reg->numBytes );
161 memcpy( &page[offset], reg->initValue.a, reg->numBytes );
163 offset += reg->numBytes;
168 /* Finally clone mirror groups */
169 for( unsigned i=0; i<block->numRegs; i++ ) {
170 regdef_t reg = block->regs[i];
171 if( reg->mode == REG_MIRROR ) {
172 unsigned offset = reg->offset;
173 unsigned target = reg->initValue.i;
174 for( unsigned i=0; i<reg->numElements; i++ ) {
175 memcpy( &page[offset], &page[target], reg->numBytes );
176 offset += reg->stride;
185 void fwrite_dump( unsigned char *data, unsigned int length, FILE *f )
189 for( i =0; i<length; i+=16 ) {
190 if( i >= 16 && i < length-16 && memcmp( &data[i], &data[i-16], 16) == 0 ) {
195 fprintf( f, " ** \n" );
198 fprintf( f, "%08X:", i);
199 for( j=i; j<i+16; j++ ) {
200 if( j != i && (j % 4) == 0 )
203 fprintf( f, " %02X", (unsigned int)(data[j]) );
208 for( j=i; j<i+16 && j<length; j++ ) {
209 fprintf( f, "%c", isprint(data[j]) ? data[j] : '.' );
215 static const char *file_top = "/**\n"
216 " * This file was automatically generated by genmmio, do not edit\n"
219 void write_source( FILE *f, GList *blocks, const char *header_filename )
221 fputs( file_top, f );
222 fprintf( f, "\n#include \"%s\"\n\n", header_filename );
224 for( GList *ptr = blocks; ptr != NULL; ptr = ptr->next ) {
225 regblock_t block = (regblock_t)ptr->data;
226 /* Generate the mmio region struct */
227 fprintf( f, "void FASTCALL mmio_region_%s_write(uint32_t, uint32_t);\n", block->name );
228 fprintf( f, "void FASTCALL mmio_region_%s_write_word(uint32_t, uint32_t);\n", block->name );
229 fprintf( f, "void FASTCALL mmio_region_%s_write_byte(uint32_t, uint32_t);\n", block->name );
230 fprintf( f, "void FASTCALL mmio_region_%s_write_burst(uint32_t, unsigned char *);\n", block->name );
231 fprintf( f, "int32_t FASTCALL mmio_region_%s_read(uint32_t);\n", block->name );
232 fprintf( f, "int32_t FASTCALL mmio_region_%s_read_word(uint32_t);\n", block->name );
233 fprintf( f, "int32_t FASTCALL mmio_region_%s_read_byte(uint32_t);\n", block->name );
234 fprintf( f, "void FASTCALL mmio_region_%s_read_burst(unsigned char *, uint32_t);\n", block->name );
235 fprintf( f, "struct mmio_region mmio_region_%s = {\n", block->name );
236 fprintf( f, " \"%s\", \"%s\", %08x, {\n", block->name,
237 block->description == NULL ? block->name : block->description,
239 fprintf( f, " mmio_region_%s_read, mmio_region_%s_write,\n", block->name, block->name );
240 fprintf( f, " mmio_region_%s_read_word, mmio_region_%s_write_word,\n", block->name, block->name );
241 fprintf( f, " mmio_region_%s_read_byte, mmio_region_%s_write_byte,\n", block->name, block->name );
242 fprintf( f, " mmio_region_%s_read_burst, mmio_region_%s_write_burst,\n", block->name, block->name );
243 fprintf( f, " unmapped_prefetch, mmio_region_%s_read_byte },\n", block->name );
244 fprintf( f, " NULL, NULL, {\n" );
245 for( unsigned i=0; i<block->numRegs; i++ ) {
246 regdef_t reg = block->regs[i];
247 if( reg->mode != REG_CONST ) {
249 const char *regname = reg->name;
250 if( regname == NULL ) {
252 snprintf( tmp, sizeof(tmp), "%s_%03X", block->name, reg->offset );
255 fprintf( f, " { \"%s\", \"%s\", %d, 0x%03x, 0x%08x, %s },\n", regname,
256 reg->description == NULL ? regname : reg->description,
257 reg->numBytes*8, reg->offset, (unsigned)reg->initValue.i,
258 (reg->mode == REG_RW ? "PORT_MRW" : (reg->mode == REG_WO ? "PORT_W" : "PORT_R")) );
261 fprintf( f, " }, NULL };\n" );
266 void write_header( FILE *f, GList *blocks )
268 fputs( file_top, f );
270 fputs( "\n#include \"mmio.h\"\n\n", f );
272 for( GList *ptr = blocks; ptr != NULL; ptr = ptr->next ) {
273 regblock_t block = (regblock_t)ptr->data;
274 fprintf( f, "extern struct mmio_region mmio_region_%s;\n", block->name );
275 fprintf( f, "enum mmio_region_%s_port_t {\n", block->name );
276 for( unsigned i=0; i<block->numRegs; i++ ) {
277 regdef_t reg = block->regs[i];
278 if( reg->name != NULL ) {
279 fprintf( f, " %s = 0x%03x,\n", reg->name, reg->offset );
282 fprintf( f, "};\n" );
286 void write_test( FILE *f, GList *blocks )
292 int main(int argc, char *argv[])
294 const char *header = NULL;
295 const char *output = NULL;
296 gboolean check_only = FALSE;
297 int verbose = 0, opt;
298 GList *block_list = NULL;
300 while( (opt = getopt_long( argc, argv, short_options, long_options, NULL )) != -1 ) {
320 if( optind == argc ) {
325 if( output == NULL ) {
326 output = makeFilename(argv[optind],".c");
328 if( header == NULL ) {
329 header = makeFilename(argv[optind],".h");
332 for( ; optind < argc; optind++ ) {
333 block_list = ioparse(argv[optind], block_list);
337 dump_register_blocks(stdout, block_list, verbose>1);
340 int errors = check_registers(block_list);
342 fprintf( stderr, "Aborting due to validation errors\n" );
347 FILE *f = fopen( output, "wo" );
349 fprintf( stderr, "Unable to open output file '%s': %s\n", output, strerror(errno) );
352 write_source( f, block_list, header );
355 f = fopen( header, "wo" );
357 fprintf( stderr, "Unable to open header file '%s': %s\n", header, strerror(errno) );
360 write_header( f, block_list );
364 for( GList *ptr = block_list; ptr != NULL; ptr = ptr->next ) {
365 unsigned char *data = build_page_initializer((regblock_t)ptr->data);
366 fwrite_dump( data, LXDREAM_PAGE_SIZE, stdout );
.