/* * Copyright (c) 1999-2009 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ # include "vpi_config.h" # include "vpi_user.h" # include "sys_priv.h" # include # include # include # include # include # include "sys_readmem_lex.h" static void get_mem_params(vpiHandle argv, vpiHandle callh, char *name, char **fname, vpiHandle *mitem, vpiHandle *start_item, vpiHandle *stop_item) { /* Get the first parameter (file name). */ *fname = get_filename(callh, name, vpi_scan(argv)); /* Get the second parameter (memory). */ *mitem = vpi_scan(argv); /* Get optional third parameter (start address). */ *start_item = vpi_scan(argv); if (*start_item) { /* Warn the user if they gave a real value for the start * address. */ switch (vpi_get(vpiType, *start_item)) { case vpiConstant: case vpiParameter: if (vpi_get(vpiConstType, *start_item) != vpiRealConst) break; case vpiRealVar: vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's third argument (start address) is a real " "value.\n", name); } /* Get optional forth parameter (finish address). */ *stop_item = vpi_scan(argv); if (*stop_item) { /* Warn the user if they gave a real value for the finish * address. */ switch (vpi_get(vpiType, *stop_item)) { case vpiConstant: case vpiParameter: if (vpi_get(vpiConstType, *stop_item) != vpiRealConst) { break; } case vpiRealVar: vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's fourth argument (finish address) is a " "real value.\n", name); } vpi_free_object(argv); } } else { *stop_item = 0; } } static int process_params(vpiHandle mitem, vpiHandle start_item, vpiHandle stop_item, vpiHandle callh, char *name, int *start_addr, int *stop_addr, int *addr_incr, int *min_addr, int *max_addr) { s_vpi_value val; int left_addr, right_addr; /* Get left addr of memory */ val.format = vpiIntVal; vpi_get_value(vpi_handle(vpiLeftRange, mitem), &val); left_addr = val.value.integer; /* Get right addr of memory */ val.format = vpiIntVal; vpi_get_value(vpi_handle(vpiRightRange, mitem), &val); right_addr = val.value.integer; /* Get start_addr, stop_addr and addr_incr */ if (! start_item) { *start_addr = left_addr right_addr)) { vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s: Standard inconsistency, following 1364-2005.\n", name); } /* Check that start_addr and stop_addr are within the memory range */ if (left_addr < right_addr) { if (*start_addr < left_addr || *start_addr > right_addr) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s: Start address %d is out of bounds for memory " "\'%s[%d:%d]\'!\n", name, *start_addr, vpi_get_str(vpiFullName, mitem), left_addr, right_addr); return 1; } if (*stop_addr < left_addr || *stop_addr > right_addr) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s: Finish address %d is out of bounds for memory " "\'%s[%d:%d]\'!\n", name, *stop_addr, vpi_get_str(vpiFullName, mitem), left_addr, right_addr); return 1; } } else { if (*start_addr < right_addr || *start_addr > left_addr) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s: Start address %d is out of bounds for memory " "\'%s[%d:%d]\'!\n", name, *start_addr, vpi_get_str(vpiFullName, mitem), left_addr, right_addr); return 1; } if (*stop_addr < right_addr || *stop_addr > left_addr) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s: Finish address %d is out of bounds for memory " "\'%s[%d:%d]\'!\n", name, *stop_addr, vpi_get_str(vpiFullName, mitem), left_addr, right_addr); return 1; } } return 0; } static PLI_INT32 sys_mem_compiletf(PLI_BYTE8*name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle arg; /* Check that there is a file name argument. */ if (argv == 0) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires two arguments.\n", name); vpi_control(vpiFinish, 1); return 0; } if (! is_string_obj(vpi_scan(argv))) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's first argument must be a file name (string).\n", name); vpi_control(vpiFinish, 1); } /* Check that there is a memory argument. */ arg = vpi_scan(argv); if (! arg) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s requires a second (memory) argument.\n", name); vpi_control(vpiFinish, 1); return 0; } if (vpi_get(vpiType, arg) != vpiMemory) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's second argument must be a memory.\n", name); vpi_control(vpiFinish, 1); } /* Check if there is a starting address argument. */ arg = vpi_scan(argv); if (! arg) return 0; if (! is_numeric_obj(arg)) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's third argument must be a start address " "(numeric).\n", name); vpi_control(vpiFinish, 1); } /* Check if there is a finish address argument. */ arg = vpi_scan(argv); if (! arg) return 0; if (! is_numeric_obj(arg)) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s's fourth argument must be a finish address " "(numeric).\n", name); vpi_control(vpiFinish, 1); } /* Make sure there are no extra arguments. */ if (vpi_scan(argv) != 0) { char msg [64]; unsigned argc; snprintf(msg, 64, "ERROR: %s:%d:", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); argc = 1; while (vpi_scan(argv)) argc += 1; vpi_printf("%s %s takes at most four arguments.\n", msg, name); vpi_printf("%*s Found %u extra argument%s.\n", (int) strlen(msg), " ", argc, argc == 1 ? "" : "s"); vpi_control(vpiFinish, 1); } return 0; } static PLI_INT32 sys_readmem_calltf(PLI_BYTE8*name) { int code, wwid, addr; FILE*file; char *fname = 0; s_vpi_value value; vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle mitem = 0; vpiHandle start_item = 0; vpiHandle stop_item = 0; /* start_addr and stop_addr are the parameters given to $readmem in the Verilog code. When not specified, start_addr is equal to the lower of the [left,right]_addr and stop_addr is equal to the higher of the [left,right]_addr. */ int start_addr, stop_addr, addr_incr; /* min_addr and max_addr are equal to start_addr and stop_addr if start_addraval; if (addr < min_addr || addr > max_addr) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s(%s): address (0x%x) is out of range " "[0x%x:0x%x]\n", name, fname, addr, start_addr, stop_addr); goto bailout; } /* if there is an address in the memory file, then turn off any possible warnings about not having enough words to load the memory. This is standard behavior from 1364-2005. */ word_count = 0; break; case MEM_WORD: if (addr >= min_addr && addr <= max_addr) { vpiHandle word_index; word_index = vpi_handle_by_index(mitem, addr); assert(word_index); vpi_put_value(word_index, &value, 0, vpiNoDelay); if (word_count > 0) word_count -= 1; } else { vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s(%s): Too many words in the file for the " "requested range [%d:%d].\n", name, fname, start_addr, stop_addr); goto bailout; } addr += addr_incr; break; case MEM_ERROR: vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s(%s): Invalid input character: %s\n", name, fname, readmem_error_token); goto bailout; break; default: assert(0); break; } } /* Print a warning if there are not enough words in the data file. */ if (word_count > 0) { vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s(%s): Not enough words in the file for the " "requested range [%d:%d].\n", name, fname, start_addr, stop_addr); } bailout: free(value.value.vector); free(fname); fclose(file); destroy_readmem_lexor(file); return 0; } static PLI_INT32 sys_writemem_calltf(PLI_BYTE8*name) { int addr; FILE*file; char*fname = 0; unsigned cnt; s_vpi_value value; vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle mitem = 0; vpiHandle start_item = 0; vpiHandle stop_item = 0; int start_addr, stop_addr, addr_incr; int min_addr, max_addr; // Not used in this routine. /*======================================== Get parameters */ get_mem_params(argv, callh, name, &fname, &mitem, &start_item, &stop_item); if (fname == 0) return 0; /*======================================== Process parameters */ if (process_params(mitem, start_item, stop_item, callh, name, &start_addr, &stop_addr, &addr_incr, &min_addr, &max_addr)) return 0; /* Open the data file. */ file = fopen(fname, "w"); if (file == 0) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s: Unable to open %s for writing.\n", name, fname); free(fname); return 0; } if (strcmp(name,"$writememb")==0) value.format = vpiBinStrVal; else value.format = vpiHexStrVal; /*======================================== Write memory file */ cnt = 0; for(addr=start_addr; addr!=stop_addr+addr_incr; addr+=addr_incr, ++cnt) { vpiHandle word_index; if (cnt%16 == 0) fprintf(file, "// 0x%08x\n", cnt); word_index = vpi_handle_by_index(mitem, addr); assert(word_index); vpi_get_value(word_index, &value); fprintf(file, "%s\n", value.value.str); } fclose(file); free(fname); return 0; } void sys_readmem_register() { s_vpi_systf_data tf_data; tf_data.type = vpiSysTask; tf_data.tfname = "$readmemh"; tf_data.calltf = sys_readmem_calltf; tf_data.compiletf = sys_mem_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$readmemh"; vpi_register_systf(&tf_data); tf_data.type = vpiSysTask; tf_data.tfname = "$readmemb"; tf_data.calltf = sys_readmem_calltf; tf_data.compiletf = sys_mem_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$readmemb"; vpi_register_systf(&tf_data); tf_data.type = vpiSysTask; tf_data.tfname = "$writememh"; tf_data.calltf = sys_writemem_calltf; tf_data.compiletf = sys_mem_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$writememh"; vpi_register_systf(&tf_data); tf_data.type = vpiSysTask; tf_data.tfname = "$writememb"; tf_data.calltf = sys_writemem_calltf; tf_data.compiletf = sys_mem_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$writememb"; vpi_register_systf(&tf_data); }