filename | src/tools/genmach.c |
changeset | 1296:30ecee61f811 |
prev | 1104:700e16c321e5 |
next | 1298:d0eb2307b847 |
author | nkeynes |
date | Sat Jan 26 14:00:48 2013 +1000 (9 years ago) |
permissions | -rw-r--r-- |
last change | Change glib includes to #include <glib.h> rather than the individual headers, as recent glib versions are breaking on this |
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.h>
27 #include "gettext.h"
28 #include "genmach.h"
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' },
39 { NULL, 0, 0, 0 } };
41 static void print_version()
42 {
43 printf( "genmmio 0.1\n" );
44 }
46 static void print_usage()
47 {
48 print_version();
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") );
56 }
58 /**
59 * Given an input baseName of the form "path/file.blah", return
60 * a newly allocated string "file<ext>" where <ext> is the second
61 * parameter.
62 *
63 * @param baseName a non-null filename
64 * @param ext a file extension including leading '.'
65 */
66 char *makeFilename( const char *baseName, const char *ext )
67 {
68 const char *p = strrchr(baseName, '/');
69 if( p == NULL )
70 p = baseName;
71 const char *q = strchr(p, '.');
72 if( q == NULL ) {
73 return g_strdup_printf("%s%s", p, ext);
74 } else {
75 return g_strdup_printf("%.*s%s", q-p, p, ext );
76 }
77 }
79 /**
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
82 * is well-defined.
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).
88 */
89 GList *postprocess_registers( GList *blocks )
90 {
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 ) {
96 }
97 }
98 }
99 return blocks;
100 }
102 /**
103 * Check register definition semantics. Primarily this verifies that there
104 * are no overlapping definitions.
105 * @return number of errors found.
106 */
107 int check_registers( GList *registers )
108 {
109 return 0;
110 }
112 /**
113 * Dump the high-level register map to the given file.
114 */
115 void dump_register_blocks( FILE *f, GList *blocks, gboolean detail )
116 {
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,
121 block->name,
122 (block->description == NULL ? "<no description>" : block->description) );
123 if( detail ) {
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 );
131 }
132 fprintf( f, " - %s\n",
133 (reg->description == NULL ? "<no description>" : reg->description) );
134 }
135 }
136 }
137 }
139 char *build_page_initializer( regblock_t block )
140 {
141 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);
146 } else {
147 for( unsigned i=0; i<LXDREAM_PAGE_SIZE; i++ ) {
148 page[i] = block->flags.fillValue.a[i%block->flags.fillSizeBytes];
149 }
150 }
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 );
160 } else {
161 memcpy( &page[offset], reg->initValue.a, reg->numBytes );
162 }
163 offset += reg->numBytes;
164 }
165 }
166 }
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;
177 }
178 }
179 }
181 return page;
182 }
185 void fwrite_dump( unsigned char *data, unsigned int length, FILE *f )
186 {
187 unsigned int i, j;
188 int skipped = 0;
189 for( i =0; i<length; i+=16 ) {
190 if( i >= 16 && i < length-16 && memcmp( &data[i], &data[i-16], 16) == 0 ) {
191 skipped++;
192 continue;
193 }
194 if( skipped ) {
195 fprintf( f, " ** \n" );
196 skipped = 0;
197 }
198 fprintf( f, "%08X:", i);
199 for( j=i; j<i+16; j++ ) {
200 if( j != i && (j % 4) == 0 )
201 fprintf( f, " " );
202 if( j < length )
203 fprintf( f, " %02X", (unsigned int)(data[j]) );
204 else
205 fprintf( f, " " );
206 }
207 fprintf( f, " " );
208 for( j=i; j<i+16 && j<length; j++ ) {
209 fprintf( f, "%c", isprint(data[j]) ? data[j] : '.' );
210 }
211 fprintf( f, "\n" );
212 }
213 }
215 static const char *file_top = "/**\n"
216 " * This file was automatically generated by genmmio, do not edit\n"
217 " */\n";
219 void write_source( FILE *f, GList *blocks, const char *header_filename )
220 {
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,
238 block->address );
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, 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 ) {
248 char tmp[32];
249 const char *regname = reg->name;
250 if( regname == NULL ) {
251 regname = tmp;
252 snprintf( tmp, sizeof(tmp), "%s_%03X", block->name, reg->offset );
253 }
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")) );
259 }
260 }
261 fprintf( f, " }, NULL };\n" );
262 }
264 }
266 void write_header( FILE *f, GList *blocks )
267 {
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 );
280 }
281 }
282 fprintf( f, "};\n" );
283 }
284 }
286 void write_test( FILE *f, GList *blocks )
287 {
290 }
292 int main(int argc, char *argv[])
293 {
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 ) {
301 switch(opt) {
302 case 'c':
303 check_only = TRUE;
304 break;
305 case 'd':
306 header = optarg;
307 break;
308 case 'h':
309 print_usage();
310 exit(0);
311 case 'o':
312 output = optarg;
313 break;
314 case 'v':
315 verbose++;
316 break;
317 }
318 }
320 if( optind == argc ) {
321 print_usage();
322 exit(1);
323 }
325 if( output == NULL ) {
326 output = makeFilename(argv[optind],".c");
327 }
328 if( header == NULL ) {
329 header = makeFilename(argv[optind],".h");
330 }
332 for( ; optind < argc; optind++ ) {
333 block_list = ioparse(argv[optind], block_list);
334 }
336 if( verbose ) {
337 dump_register_blocks(stdout, block_list, verbose>1);
338 }
340 int errors = check_registers(block_list);
341 if( errors != 0 ) {
342 fprintf( stderr, "Aborting due to validation errors\n" );
343 return 2;
344 }
346 if( !check_only ) {
347 FILE *f = fopen( output, "wo" );
348 if( f == NULL ) {
349 fprintf( stderr, "Unable to open output file '%s': %s\n", output, strerror(errno) );
350 return 3;
351 }
352 write_source( f, block_list, header );
353 fclose(f);
355 f = fopen( header, "wo" );
356 if( f == NULL ) {
357 fprintf( stderr, "Unable to open header file '%s': %s\n", header, strerror(errno) );
358 return 4;
359 }
360 write_header( f, block_list );
361 fclose(f);
362 }
364 for( GList *ptr = block_list; ptr != NULL; ptr = ptr->next ) {
365 char *data = build_page_initializer((regblock_t)ptr->data);
366 fwrite_dump( data, LXDREAM_PAGE_SIZE, stdout );
367 g_free(data);
368 }
369 return 0;
370 }
.