void Log_printf(char *fmt, ...) //******************** // Wrapper around vfprintf that checks for any file I/O errors. Since the // printf() style functions will occasionally flush the stdio buffers, they // can return a system level error (such as disk full). Using a wrapper // function that checks for errors immediately after each printf() guarantees // that errno will still be valid and that strerror() wlll produce a useful // message to the user. { char strBuffer[MAXBUF]; va_list args; // Do nothing if the log file could not be opened or was already closed // due to a previous error. if(!File) { return; } // Call the real fvprintf function to do the actual printing va_start(args, fmt); vfprintf(File, fmt, args); // If any I/O error occurred, break with error message and close log file if(ferror(File)) { snprintf(strBuffer, MAXBUF, "Could not write \"%s\" file: %s", FILE_NAME, strerror(errno)); BREAK(strBuffer); Close_file(); } va_end(args); }
void On_simulation_begin() //************************ // VMLAB informs you that the simulation is starting. Initialize pin values // here Open files; allocate memory, etc. // The first instance to enter this function is responsible for opening the // single log file and initializing any global data. Although the log filename // doesn't change with each simulation, it's better to open it here and close it // in On_simulation_end(). This allows the user to delete or move the log file // without having to close or rebuild the project. { char strBuffer[MAXBUF]; // Force the initial value of data input to be logged at time step 0 VAR(Log_data) = -1; // Keep track of how many instances have already been created VAR(Instance_number) = Instance_count; Instance_count++; // Because the VCD file format uses a single ASCII character to identify each // signal, it limits the number of vcdlog instances that can be used in a // project if(VAR(Instance_number) + MIN_ID > MAX_ID) { snprintf(strBuffer, MAXBUF, "Too many instances (max %d)", MAX_ID - MIN_ID + 1); BREAK(strBuffer); Close_file(); } // The first instance to have its On_simulation_begin() called is responsible // for opening the log file and initializing global variables. if(Instance_count == 1) { Total_time = 0; // Setting Log_time to -1 forces the first instance entering // On_time_step(), to finish writing the VCD header section. Log_time = -1; // Create or overwrite log file in current directory File = fopen(FILE_NAME, "w"); // We can still run if the file won't open; we just can't log anything. if(!File) { snprintf(strBuffer, MAXBUF, "Could not create \"%s\" file: %s", FILE_NAME, strerror(errno)); BREAK(strBuffer); } // Write out the global VCD file header Log_printf("$version VMLAB vcdlog component $end\n"); Log_printf("$timescale 1 %s $end\n", TIME_UNITS); Log_printf("$scope module vmlab $end\n"); } // Write out per instance part of the VCD header that contains the variable // name and the ASCII identifier. Log_printf("$var wire 1 %c %s $end\n", VAR(Instance_number) + MIN_ID, GET_INSTANCE()); }
void On_simulation_end() //********************** // Undo here the operations done at On_simulation_begin: free memory, close // files, etc. { free(VAR(Sample_buffer)); Close_file(); }
void On_simulation_end() //********************** // Undo here the operations done at On_simulation_begin: free memory, close // files, etc. { // Do nothing if the log file could not be opened if(!VAR(File)) { return; } // Write the last pending entry to the log Write_log(0); // Close the log file so it can be moved or deleted Close_file(); }
int create_index_file(char* filename) { FILE *fd; if ((fd = fopen(filename,"wx")) != NULL) fprintf(fd, "<html><body><h1>ur file has been removed. so get out of here!</h1></body></html>\n"); else if (errno == EEXIST) { msg(MSG_ERROR, "index.html already exists\n"); return -1; } else { msg(MSG_ERROR, "could not create the index.html: %s", strerror(errno)); return -1; } fflush(fd); Close_file(fd); return 0; }
void On_simulation_end() //********************** // Undo here the operations done at On_simulation_begin: free memory, close // files, etc. { // Write the total elapsed simulation time at the end of the VCD file, // making sure to convert it to nanoseconds. Without this final "delay" // in the VCD file, any bit value changes in the last time step might // not be visible in some waveform viewers like GTKWave. Log_printf("#%.0lf\n", Total_time * TIME_MULT); // Since there is nothing else left to do here, except for closing the // log file, we just let the first instance to enter On_simulation_end() // close the file and reset the global Instance_count in preparation for // another simulation. Close_file(); Instance_count = 0; }
void On_simulation_begin() //************************ // VMLAB informs you that the simulation is starting. Initialize pin values // here Open files; allocate memory, etc. // Although the wav filename doesn't change with each simulation, it's better to // open it here and close it in On_simulation_end(). This allows the user to // delete or move the log file without having to close or rebuild the project. { char strBuffer[MAXBUF]; // Open existing wav file in current directory. The libsndfile API requires // the SF_INFO.format field set to 0 before opening a file for read access. VAR(File_info).format = 0; snprintf(strBuffer, MAXBUF, "%s.wav", GET_INSTANCE()); VAR(File) = sf_open(strBuffer, SFM_READ, &VAR(File_info)); // We can still run if the file won't open; output stays at POWER()/2. if(!VAR(File)) { snprintf(strBuffer, MAXBUF, "Could not open \"%s.wav\" file: %s", GET_INSTANCE(), sf_strerror(VAR(File))); BREAK(strBuffer); return; } // If the WAV file contains more than one channel, print an error mssage // that the additional channels will be ignored. if(VAR(File_info).channels != 1) { snprintf(strBuffer, MAXBUF, "File \"%s.wav\" has multiple channels; " "only first (left) channel used", GET_INSTANCE()); PRINT(strBuffer); } // Allocate buffer large enough to hold a single sample across all the // channels. The libsndfile library requires that all the channels are read // at once, and it provides no way to ignore unwanted channels. VAR(Sample_buffer) = (double *) malloc(sizeof(double) * VAR(File_info).channels); if(!VAR(Sample_buffer)) { BREAK("Error allocating memory buffer"); Close_file(); return; } }
void On_remind_me(double pTime, int pData) //*************************************** // VMLAB notifies about a previouly sent REMIND_ME() function. // Read the next voltage sample from the WAV file, set the analog voltage on the // output pin, and schedule another output update based on the sampling rate of // the input wav file. { char strBuffer[MAXBUF]; // Do nothing if the wav file is closed due to an error if(!VAR(File)) { return; } // Read the next voltage sample from the wav file. If an error occurs or // EOF is reached, then the wav file is closed, the output voltage remains // set to the previous value and no more output updates are scheduled. if(sf_readf_double(VAR(File), VAR(Sample_buffer), 1) != 1) { // Break with an error message if an actual I/O or decode error occurred if(sf_error(VAR(File))) { snprintf(strBuffer, MAXBUF, "Error reading \"%s.wav\" file: %s", GET_INSTANCE(), sf_strerror(VAR(File))); BREAK(strBuffer); } // Close the file on either an error or a normal end-of-file Close_file(); return; } // The voltage in VMLAB ranges from 0 to POWER(), while the sample that // libsndfile returns ranges from -1 to +1. Only the sample from the // first (left) channel is used here. Samples from additional channels // are simply discarded. SET_VOLTAGE(DATA, (*VAR(Sample_buffer) + 1) * 0.5 * POWER()); // Schedule the next On_remind_me() based on the sampling rate REMIND_ME(1.0 / VAR(File_info).samplerate); }
void Write_log(double pTime) //******************** // If the elapsed time "pTime" of the current time step is different from the // previous time step at "VAR(Log_time)" then write a log entry for the previous // time step. Because On_digital_in_edge() may be called multiple times for the // same time step, we want to delay writing any log entry until we know the time // step has been fully simulated. { char strBuffer[MAXBUF]; double logCycle; // Do nothing if log file could not be opened or if the next time step has // not been reached yet. if(!VAR(File) || pTime == VAR(Log_time)) { return; } // The elapsed time (in seconds) is converted to a cycle count based on the // MCU's clock rate (specified as a parameter). The "- VAR(offset)" subtracts // out the initial power on delay from the cycle count. Also, to avoid any // floating point round off errors, the fprintf() is used to round up the // result to the closest integer instead of using an integer cast. logCycle = (VAR(Log_time) - VAR(Clock_delay)) / VAR(Clock_period); fprintf(VAR(File), "%09.0lf:%02X\n", logCycle, VAR(Log_data)); // If any errors occur with writing the file, then break with an error // message and close the file to prevent any further logging. if(ferror(VAR(File))) { snprintf(strBuffer, MAXBUF, "Could not write \"%s.log\" file: %s", GET_INSTANCE(), strerror(errno)); BREAK(strBuffer); Close_file(); } // Record current time so next log entry isn't written until next time step VAR(Log_time) = pTime; }