URL
https://opencores.org/ocsvn/or1k/or1k/trunk
Subversion Repositories or1k
[/] [or1k/] [trunk/] [or1ksim/] [cpu/] [or32/] [generate.c] - Rev 1290
Go to most recent revision | Compare with Previous | Blame | View Log
/* generate.c -- generates file execgen.c from instruction set Copyright (C) 1999 Damjan Lampret, lampret@opencores.org This file is part of OpenRISC 1000 Architectural Simulator. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include <stdlib.h> #include <stdio.h> #include <string.h> #include <stdarg.h> #include <ctype.h> #include "config.h" #include "opcode/or32.h" #include "abstract.h" #include "labels.h" #include "parse.h" #include "execute.h" #define LEAF_FLAG (0x80000000) #define SHIFT {int i; for (i = 0; i < level; i++) fprintf (fo, " ");} extern unsigned long *automata; extern struct temp_insn_struct { unsigned long insn; unsigned long insn_mask; int in_pass; } *ti; static char *in_file; unsigned long op[MAX_OPERANDS]; int num_op; inline void debug(int level, const char *format, ...) { char *p; va_list ap; #if DEBUG if ((p = malloc(1000)) == NULL) return; va_start(ap, format); (void) vsnprintf(p, 1000, format, ap); va_end(ap); printf("%s\n", p); fflush(stdout); free(p); #endif } /* Whether this instruction stores something in register */ static int write_to_reg = 0; static int olevel; /* Following functions recursivelly searches for substrings eval_operand and set_operand (see functions with the same name in execute.c) and replaces them with optimized code. */ char *replace_operands (FILE *fo, char *str) { int replace = 0; if (*str == '}') {olevel--;} else if (*str == '{') {olevel++;} else if (strncmp ("eval_operand", str, 12) == 0) { replace = 1; str += 12; } else if (strncmp ("set_operand", str, 11) == 0) { replace = 2; str += 11; } else if (strncmp ("get_operand", str, 11) == 0) { replace = 10; str += 11; } if (replace) { int width, oper; if (replace < 10) { sscanf (str, "%i(%i", &width, &oper); while (*str && *str != '(') str++; while (*str && *str != ',') str++; str++; } else { sscanf (str, "(%i)", &oper); while (*str && *str != ')') str++; } if (replace == 1) { if (op[oper] & OPTYPE_DIS) { fprintf (fo, "eval_mem%i (%c", width, 'a' + oper); } else { if (op[oper] & OPTYPE_REG) { fprintf (fo, "(reg[%c]", 'a' + oper); } else { fprintf (fo, "(%c", 'a' + oper); } } } else if (replace == 2) { op[oper] |= OPTYPE_DST; if (op[oper] & OPTYPE_DIS) { fprintf (fo, "set_mem%i(%c,", width, 'a' + oper); } else if (op[oper] & OPTYPE_REG) { fprintf (fo, "reg[%c] = (", 'a' + oper); write_to_reg = 1; } else { fprintf (stderr, "Invalid operand type.\n"); exit (1); } while (*str != ',') str = replace_operands (fo, str) + 1; } else { fprintf (fo, "%c", 'a' + oper); } if (replace < 10) { while (*str && *str != ')') str++; if (op[oper] & OPTYPE_DIS) fprintf (fo, ", &breakpoint)"); else fprintf (fo, ")"); } } else { fputc (*str, fo); } return str; } /* Generates a execute sequence for one instruction */ int output_function (FILE *fo, const char *func_name, int level) { FILE *fi; if ((fi = fopen (in_file, "rt")) == NULL) { printf("could not open file\n"); return 1; }; while (!feof (fi)) { char line[10000], *str = line; fgets (str, sizeof (line), fi); line[sizeof(line) - 1] = 0; if (strncmp (str, "INSTRUCTION (", 13) == 0) { char *s; str += 13; while (isspace (*str)) str++; s = str; while (*s && *s != ')') s++; *s = 0; while (isspace(*(s - 1))) s--; *s = 0; if (strcmp (str, func_name) == 0) { olevel = 1; str += strlen (str) + 1; while (isspace (*str)) str++; s = str; while (*s && *s != '\n' && *s != '\r') s++; *s = 0; while (isspace(*(s - 1))) s--; *s = 0; fprintf (fo, "%s", str); fprintf (fo, " /* \"%s\" */\n", func_name); SHIFT; do { fgets (line, sizeof (line), fi); line[sizeof(line) - 1] = 0; for (str = line; *str; str++) { str = replace_operands (fo, str); } SHIFT; } while (olevel); fclose(fi); return 0; } } } fprintf (fo, "{\n"); level++; SHIFT; fprintf (fo, "%s ();\n", func_name); level--; SHIFT; fprintf (fo, "}"); fclose(fi); return 0; } /* Parses and puts operands into op[] structure. Replacement for eval_operands routine. */ static void gen_eval_operands (FILE *fo, int insn_index, int level) { struct insn_op_struct *opd = op_start[insn_index]; int dis = 0; int no = 0; int firstd = 1; while (1) { int nbits = 0, first = 1; while (1) { SHIFT; fprintf (fo, "tmp %s= ((insn >> %i) & 0x%08x) << %i;\n", first ? "" : "|", opd->type & OPTYPE_SHR, (1 << opd->data) - 1, nbits); nbits += opd->data; if (opd->type & OPTYPE_OP) break; opd++; first = 0; } /* Do we have to sign extend? */ if (opd->type & OPTYPE_SIG) { int sbit = (opd->type & OPTYPE_SBIT) >> OPTYPE_SBIT_SHR; SHIFT; fprintf (fo, "if (tmp & (1 << %i)) tmp |= 0xFFFFFFFF << %i; /* Sign extend */\n", sbit, sbit); } if (opd->type & OPTYPE_DIS) { /* We have to read register later. */ SHIFT; fprintf (fo, "data %s= tmp;\n", firstd ? "" : "+"); firstd = 0; dis = 1; } else { if (dis && (opd->type & OPTYPE_REG)) { if (MAX_GPRS == (1 << nbits)) { SHIFT; fprintf (fo, "%c = data + reg [tmp];\n", 'a' + no); } else { SHIFT; fprintf (fo, "%c = data + eval_reg32 (tmp);\n", 'a' + no); } } else { SHIFT; fprintf (fo, "%c = tmp;\n", 'a' + no); } op[no] = opd->type | (dis ? OPTYPE_DIS : 0); no++; firstd = 1; dis = 0; } if(opd->type & OPTYPE_LAST) goto last; opd++; } last: num_op = no; } /* Generates decode and execute for one instruction instance */ int output_call (FILE *fo, int index, int level) { int i; printf ("%i:%s\n", index, insn_name (index)); fprintf (fo, "{\n"); level++; if (index >= 0) { SHIFT; fprintf (fo, "unsigned long data, tmp;\n"); SHIFT; fprintf (fo, "unsigned long a, b, c, d, e; /* operands */\n"); } write_to_reg = 0; if (index >= 0) gen_eval_operands (fo, index, level); else num_op = 0; SHIFT; if (index < 0) output_function (fo, "l_invalid", level); else output_function (fo, or32_opcodes[index].function_name, level); fprintf (fo, "\n"); SHIFT; fprintf (fo, "if (do_stats) {\n"); level++; SHIFT; fprintf (fo, "num_op = %i;\n", num_op); if (num_op) {SHIFT; fprintf (fo, " op = ¤t->op[0];\n");} SHIFT; fprintf (fo, "current->insn_index = %i; /* \"%s\" */\n", index, insn_name (index)); for (i = 0; i < num_op; i++) { SHIFT; fprintf (fo, "op[%i] = %c;\n", i, 'a' + i); SHIFT; fprintf (fo, "op[%i + MAX_OPERANDS] = 0x%08x;\n", i, op[i]); } SHIFT; fprintf (fo, "analysis(current);\n"); level--; SHIFT; fprintf (fo, "}\n"); if (write_to_reg) { SHIFT; fprintf (fo, "reg[0] = 0; /* Repair in case we changed it */\n"); } level--; SHIFT; fprintf (fo, "}"); return 0; } /* Generates .c file header */ int generate_header (FILE *fo) { fprintf (fo, "/* This file was automatically generated by generate (see cpu/or32/generate.c) */\n\n"); fprintf (fo, "static inline void decode_execute (struct iqueue_entry *current)\n{\n"); fprintf (fo, " unsigned long insn = current->insn;\n"); return 0; } /* Generates .c file footer */ int generate_footer (FILE *fo) { fprintf (fo, "}\n"); return 0; } /* Decodes all instructions and generates code for that. This function is similar to insn_decode, except it decodes all instructions. */ static int generate_body (FILE *fo, unsigned long *a, unsigned long cur_mask, int level) { int i; if (!(*a & LEAF_FLAG)) { unsigned int shift = *a++; unsigned int mask = *a++; int prev_invalid = 0; fprintf (fo, "\n"); SHIFT; fprintf (fo, "/* (insn >> %i) & 0x%x */\n", shift, mask); SHIFT; fprintf (fo, "switch ((insn >> %i) & 0x%x) {\n", shift, mask); level++; /* Print each case recursively */ for (i = 0; i <= mask; i++, a++) { /* Group invalid instruction decodes together */ if (!*a) { if (prev_invalid) fprintf (fo, "\n"); prev_invalid = 1; SHIFT; fprintf (fo, "case 0x%02x: ", i); } else { if (prev_invalid) { if (output_call (fo, -1, level)) return 1; fprintf (fo, " break;\n"); } SHIFT; fprintf (fo, "case 0x%02x: ", i); if (generate_body (fo, automata + *a, cur_mask | (mask << shift), level + 1)) return 1; prev_invalid = 0; } } if (prev_invalid) { if (output_call (fo, -1, level)) return 1; fprintf (fo, " break;\n"); } level--; if (level > 1) fprintf (fo, "} break;\n"); else fprintf (fo, "}\n"); } else { i = *a & ~LEAF_FLAG; /* Final check - do we have direct match? (based on or32_opcodes this should be the only possibility, but in case of invalid/missing instruction we must perform a check) */ if (ti[i].insn_mask != cur_mask) { fprintf (fo, "\n"); SHIFT; fprintf (fo, "/* Not unique: real mask %08x and current mask %08x differ - do final check */\n", ti[i].insn_mask, cur_mask); SHIFT; fprintf (fo, "if ((insn & 0x%08x) == 0x%08x) ", ti[i].insn_mask, ti[i].insn); if (output_call (fo, i, level)) return 1; // Fail fprintf (fo, " else "); if (output_call (fo, -1, level)) return 1; // Fail } else { if (output_call (fo, i, level - 1)) return 1; // Fail } fprintf (fo, " break;\n"); } return 0; } /* Main function; it takes two parameters: input_file(possibly insnset.c) output_file(possibly execgen.c)*/ int main (int argc, char *argv[]) { FILE *fo; if (argc != 3) { fprintf (stderr, "USAGE: generate input_file(possibly insnset.c) output_file(possibly execgen.c)\n"); exit (-1); } in_file = argv[1]; if (!(fo = fopen (argv[2], "wt+"))) { fprintf (stderr, "Cannot create '%s'.\n", argv[2]); exit (1); } build_automata (); if (generate_header (fo)) {fprintf (stderr, "generate_header\n"); return 1;} if (generate_body (fo, automata, 0, 1)) {fprintf (stderr, "generate_body\n"); return 1;} if (generate_footer (fo)) {fprintf (stderr, "generate_footer\n"); return 1;} fclose (fo); destruct_automata (); return 0; }
Go to most recent revision | Compare with Previous | Blame | View Log