filename | src/tools/genmach.c |
changeset | 1104:700e16c321e5 |
next | 1296:30ecee61f811 |
author | nkeynes |
date | Sat Sep 11 09:44:21 2010 +1000 (13 years ago) |
permissions | -rw-r--r-- |
last change | GTK: Comment out non-existent setting menu-items, to reduce potential confusion |
view | annotate | diff | log | raw |
1 /**
2 * $Id$
3 *
4 * mmio register code generator
5 *
6 * Copyright (c) 2010 Nathan Keynes.
7 *
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.
12 *
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.
17 */
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <unistd.h>
23 #include <getopt.h>
24 #include <ctype.h>
25 #include <errno.h>
26 #include <glib/gstrfuncs.h>
27 #include <glib/glist.h>
28 #include "gettext.h"
29 #include "genmach.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' },
40 { NULL, 0, 0, 0 } };
42 static void print_version()
43 {
44 printf( "genmmio 0.1\n" );
45 }
47 static void print_usage()
48 {
49 print_version();
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") );
57 }
59 /**
60 * Given an input baseName of the form "path/file.blah", return
61 * a newly allocated string "file<ext>" where <ext> is the second
62 * parameter.
63 *
64 * @param baseName a non-null filename
65 * @param ext a file extension including leading '.'
66 */
67 char *makeFilename( const char *baseName, const char *ext )
68 {
69 const char *p = strrchr(baseName, '/');
70 if( p == NULL )
71 p = baseName;
72 const char *q = strchr(p, '.');
73 if( q == NULL ) {
74 return g_strdup_printf("%s%s", p, ext);
75 } else {
76 return g_strdup_printf("%.*s%s", q-p, p, ext );
77 }
78 }
80 /**
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
83 * is well-defined.
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).
89 */
90 GList *postprocess_registers( GList *blocks )
91 {
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 ) {
97 }
98 }
99 }
100 return blocks;
101 }
103 /**
104 * Check register definition semantics. Primarily this verifies that there
105 * are no overlapping definitions.
106 * @return number of errors found.
107 */
108 int check_registers( GList *registers )
109 {
110 return 0;
111 }
113 /**
114 * Dump the high-level register map to the given file.
115 */
116 void dump_register_blocks( FILE *f, GList *blocks, gboolean detail )
117 {
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,
122 block->name,
123 (block->description == NULL ? "<no description>" : block->description) );
124 if( detail ) {
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 );
132 }
133 fprintf( f, " - %s\n",
134 (reg->description == NULL ? "<no description>" : reg->description) );
135 }
136 }
137 }
138 }
140 char *build_page_initializer( regblock_t block )
141 {
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);
147 } else {
148 for( unsigned i=0; i<LXDREAM_PAGE_SIZE; i++ ) {
149 page[i] = block->flags.fillValue.a[i%block->flags.fillSizeBytes];
150 }
151 }
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 );
161 } else {
162 memcpy( &page[offset], reg->initValue.a, reg->numBytes );
163 }
164 offset += reg->numBytes;
165 }
166 }
167 }
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;
178 }
179 }
180 }
182 return page;
183 }
186 void fwrite_dump( unsigned char *data, unsigned int length, FILE *f )
187 {
188 unsigned int i, j;
189 int skipped = 0;
190 for( i =0; i<length; i+=16 ) {
191 if( i >= 16 && i < length-16 && memcmp( &data[i], &data[i-16], 16) == 0 ) {
192 skipped++;
193 continue;
194 }
195 if( skipped ) {
196 fprintf( f, " ** \n" );
197 skipped = 0;
198 }
199 fprintf( f, "%08X:", i);
200 for( j=i; j<i+16; j++ ) {
201 if( j != i && (j % 4) == 0 )
202 fprintf( f, " " );
203 if( j < length )
204 fprintf( f, " %02X", (unsigned int)(data[j]) );
205 else
206 fprintf( f, " " );
207 }
208 fprintf( f, " " );
209 for( j=i; j<i+16 && j<length; j++ ) {
210 fprintf( f, "%c", isprint(data[j]) ? data[j] : '.' );
211 }
212 fprintf( f, "\n" );
213 }
214 }
216 static const char *file_top = "/**\n"
217 " * This file was automatically generated by genmmio, do not edit\n"
218 " */\n";
220 void write_source( FILE *f, GList *blocks, const char *header_filename )
221 {
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,
239 block->address );
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 ) {
249 char tmp[32];
250 const char *regname = reg->name;
251 if( regname == NULL ) {
252 regname = tmp;
253 snprintf( tmp, sizeof(tmp), "%s_%03X", block->name, reg->offset );
254 }
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")) );
260 }
261 }
262 fprintf( f, " }, NULL };\n" );
263 }
265 }
267 void write_header( FILE *f, GList *blocks )
268 {
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 );
281 }
282 }
283 fprintf( f, "};\n" );
284 }
285 }
287 void write_test( FILE *f, GList *blocks )
288 {
291 }
293 int main(int argc, char *argv[])
294 {
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 ) {
302 switch(opt) {
303 case 'c':
304 check_only = TRUE;
305 break;
306 case 'd':
307 header = optarg;
308 break;
309 case 'h':
310 print_usage();
311 exit(0);
312 case 'o':
313 output = optarg;
314 break;
315 case 'v':
316 verbose++;
317 break;
318 }
319 }
321 if( optind == argc ) {
322 print_usage();
323 exit(1);
324 }
326 if( output == NULL ) {
327 output = makeFilename(argv[optind],".c");
328 }
329 if( header == NULL ) {
330 header = makeFilename(argv[optind],".h");
331 }
333 for( ; optind < argc; optind++ ) {
334 block_list = ioparse(argv[optind], block_list);
335 }
337 if( verbose ) {
338 dump_register_blocks(stdout, block_list, verbose>1);
339 }
341 int errors = check_registers(block_list);
342 if( errors != 0 ) {
343 fprintf( stderr, "Aborting due to validation errors\n" );
344 return 2;
345 }
347 if( !check_only ) {
348 FILE *f = fopen( output, "wo" );
349 if( f == NULL ) {
350 fprintf( stderr, "Unable to open output file '%s': %s\n", output, strerror(errno) );
351 return 3;
352 }
353 write_source( f, block_list, header );
354 fclose(f);
356 f = fopen( header, "wo" );
357 if( f == NULL ) {
358 fprintf( stderr, "Unable to open header file '%s': %s\n", header, strerror(errno) );
359 return 4;
360 }
361 write_header( f, block_list );
362 fclose(f);
363 }
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 );
368 g_free(data);
369 }
370 return 0;
371 }
.