//*********************************************** // Delay Code Generator // for PIC microcontrollers // // 7-July-2000 by Nikolai Golovchenko // // 10-July // Banner removed // 11-July // Fixed bug when called without query string // from MS IIS 4.0 server // 14-July // 1) Embedded the minimal form in code to display it if the external page doesn't exist // 2) Implemented shared access to the log file //*********************************************** //Standard includes #include #include #include #include #include #include using namespace std; typedef list LISTSTR; //Custom includes extern "C" { #include "cgi-lib.h" #include "html-lib.h" char** CommandLineArguments; } #include "delay.h" bool isCGI; double Delay = -1; string RegName = "d"; //prefix. If temporary registers are specified, prefix is the first name LISTSTR Regs; //list of specified or used register names //(if not enough list is expanded using RegName prefix) double Clock = 4; //Clock frequency in MHz bool Routine = false; //generate as a routine bool inSeconds = true; //seconds or cycles string Name = "Delay"; //name of routine int main(int argc, char *argv[], char **envp) { llist entries = {NULL}; //clear list!!! char Error = 0; string buf; //make pointer to command line global CommandLineArguments = argv; /********* DEBUG ***************** _putenv("SERVER_SOFTWARE=Microsoft-IIS/4.0"); _putenv("SERVER_NAME=www.piclist.com"); _putenv("GATEWAY_INTERFACE=CGI/1.1"); _putenv("SERVER_PROTOCOL=HTTP/1.1"); _putenv("SERVER_PORT=80"); _putenv("REQUEST_METHOD=GET"); _putenv("PATH_TRANSLATED=C:\\Inetpub\\wwwroot"); _putenv("SCRIPT_NAME=/cgi-bin/test.exe"); _putenv("REMOTE_HOST=204.210.50.240"); _putenv("REMOTE_ADDR=204.210.50.240"); _putenv("CONTENT_LENGTH=0"); ***********************************/ //what kind of request isCGI = false; if(getenv("REQUEST_METHOD") != NULL) { isCGI = true; } /* Read form values */ read_cgi_input(&entries); //Check for very bad input if(isCGI) { //log request logRequest(); //check if form is empty if( !is_field_exists(entries, "Delay") || !is_field_exists(entries, "Type") || !is_field_exists(entries, "Clock") ) { //CGI form should have query string and these fields helpScreen(); list_clear(&entries); exit(0); } // Generate header and title html_header(); html_begin("Code Generator Results"); } else if(argc == 1 || !is_field_exists(entries, "Delay")) { //command line mode and no required parameters helpScreen(); list_clear(&entries); exit(0); } //read the form fields / options if(is_field_exists(entries, "Delay")) { Delay = atof(cgi_val(entries, "Delay")); } if(Delay <= 0) { ReportError(Error = 2); } if(is_field_exists(entries, "Type")) { buf = cgi_val(entries, "Type"); lowercaseString(buf); if(buf == "cycles") { inSeconds = false; } else if(buf != "seconds") { ReportError(Error = 1); } } buf = ""; if(is_field_exists(entries, "Regs")) { buf = cgi_val(entries, "Regs"); } //extract names to list extractRegs(buf, Regs, RegName); if(is_field_exists(entries, "Clock")) { Clock = atof(cgi_val(entries, "Clock")); if(Clock <= 0) { ReportError(Error = 3); } } if(is_field_exists(entries, "Routine")) { buf = cgi_val(entries, "Routine"); lowercaseString(buf); if(buf == "yes") { Routine = true; } } if(is_field_exists(entries, "Name")) { Name = cgi_val(entries, "Name"); } //convert delay to cycles if(Clock > 0 && Delay > 0) { double x = delayInCycles(); if(x < 1) { ReportError(Error = 4); } else if(x + 1 == x) { //double can hold this number with the resolution of 1 ReportError(Error = 5); } else if(Routine && x < 4) { //Delay too small ReportError(Error = 6); } } if(Error == 0) { /* Open preformatted block */ if(isCGI) { printf("
\n");
		}
		/* Call code generation */
		generateDelay();
		/* Close preformatted block */
		if(isCGI)
		{
			printf("
\n"); TimeStamp(); Banner(); } } else { printLinkBack(); } if(isCGI) { html_end(); } list_clear(&entries); return 0; } void printLinkBack() { char* var; var = getenv("HTTP_REFERER"); if(isCGI && var != NULL) { printf("
\n Back to form\n", var); } } void ReportError(char Err) { //char *var; if(isCGI) { printf("

"); } /* else { //Print form values printForm(); }*/ printf("Error %d: ", Err); switch(Err) { case 1: printf("Wrong type of delay. Enter 'seconds' or 'cycles'."); break; case 2: printf("Delay is zero or negative."); break; case 3: printf("Clock frequency is zero or negative."); break; case 4: printf("Delay is less than one instruction cycle."); break; case 5: printf("Number of cycles is too big."); break; case 6: printf("Delay is less than 4. Can't generate this delay as a routine."); break; case 8: break; default: break; } if(isCGI) { printf("

"); } printf("\n"); } void logRequest() { FILE* log = NULL; time_t ltime; time_t final; time_t nowTime; struct tm* now; char* buf; char* query; time(&final); nowTime = ltime = final; final += 3; //3 seconds for retries while(log == NULL && nowTime < final) { if(ltime == nowTime) { ltime++; log = _fsopen( "delay_log.csv", "a", _SH_DENYWR); //create or open file in append mode //writing is disabled for other processes } time(&nowTime); } if(log != NULL) { //file opened ok //if file is empty, create header if(_filelength(_fileno(log)) == 0) { fprintf(log, "Date,Time,Address,Client,Method,Query\n"); } //Date and time time(<ime); now = localtime(<ime); fprintf(log, "%d/%d/%d,", 1900 + now->tm_year, now->tm_mon, now->tm_mday); fprintf(log, "%d:%d:%d,", now->tm_hour, now->tm_min, now->tm_sec); //Address,Client,Method logPrint(log, "REMOTE_ADDR"); logPrint(log, "HTTP_USER_AGENT"); logPrint(log, "REQUEST_METHOD"); //Query query = getenv("QUERY_STRING"); if(query != NULL) { buf = new char[strlen(query) + 1]; strcpy(buf, query); unescape_url(buf); //remove escape sequencies fprintf(log, "%s", buf); //without comma at the end of line delete(buf); } fprintf(log, "\n"); fclose(log); } } //print env variable if not empty void logPrint(FILE* log, char* envName) { char *var; var = getenv(envName); if(var != NULL) { fprintf(log, "%s", var); } fprintf(log, ","); //comma separation } void printForm() { printf("; Delay = %g %s\n", Delay, inSeconds ? "seconds" : "instruction cycles"); printf("; Clock frequency = %g MHz\n", Clock); printf("\n"); } void TimeStamp() { time_t ltime; struct tm *gmt; /* Display UTC. */ time(<ime); gmt = gmtime(<ime); printf("; Generated by www.piclist.com/cgi-bin/delay.exe (%s version)
\n", VERSIONDATE); printf("; %s GMT
\n", asctime(gmt)); } void Banner() { #ifdef SHOW_BANNER printf("\n"); printf(" \n"); printf(" \n"); printf(" \n"); printf("
\n", "%"); printf("
\n"); printf("I hope my little code generator helps you. Would you like to help \n"); printf("me find a job in the U.S.?
\n"); printf("Nikolai Golovchenko\n"); printf("
\n"); #endif } void helpScreen() { FILE* form; char ch; if(isCGI) { html_header(); //try to open form form = fopen("delay.htm", "r"); if(form == NULL) { html_begin("Delay Code Generator"); printf("\n"); //print built-in form h1("Delay Code Generator"); printf("
"); printf(""); printf(""); printf(""); printf(""); printf("

Delay

"); printf("

"); printf("Instruction cycles

"); printf("

Seconds

Temporary registers names

"); printf("

"); printf("

Clock frequency

"); printf("

MHz

"); printf("Generate"); printf("routine   
"); printf(" 
"); printf("
"); html_end(); } else { //print external form while(!feof(form)) { fread(&ch, 1, 1, form); putchar(ch); } fclose(form); } } else { printf("--------------------------------------------------------------------\n"); printf("DELAY CODE GENERATION IN PIC ASSEMBLY\n"); printf("http://www.piclist.com/codegen/\n"); printf("--------------------------------------------------------------------\n"); printf("Command line: delay.exe {option=value} {> OutputFile}\n"); printf("Options:\n\tDelay - value of delay in cycles or seconds (no default)\n"); printf("\tType - type of delay - cycles/seconds (default: seconds )\n"); printf("\tRegs - temporary registers, separated by comma (default: d1, d2, ...)\n"); printf("\tClock - clock frequency in MHz (default: 4)\n"); printf("\tRoutine - Generate as a routine - yes/no (default: no)\n"); printf("\tName - Routine name (default: Delay)\n"); printf("\nNote:\n"); printf(" If a value contains spaces, insert quotes around the value,\n e.g. regs=\"r1 r2 r3\"\n"); printf("\nExample:\n"); printf(" delay.exe delay=3600 type=seconds clock=20 > result.asm\n"); } } void extractRegs(string& buf, LISTSTR& Regs, string& newPrefix) { char x; LISTSTR::iterator i; bool empty=true; //collect all names in the list Regs while((x = buf.find_first_of(" ,")) != -1) //find first space or comma { Regs.push_back(buf.substr(0, x)); stripBlanks(Regs.front()); if(Regs.back().empty()) { Regs.pop_back(); } buf.erase(0, x + 1); } Regs.push_back(buf); stripBlanks(Regs.front()); if(Regs.back().empty()) { Regs.pop_back(); } //change prefix if(!Regs.front().empty()) { newPrefix = Regs.front() + "_"; } //fill all other regs names for(x = Regs.size() + '0'; x < MAXNUMBEROFLOOPS + '0'; x++) { Regs.push_back(newPrefix + x); } /*i = Regs.begin(); while(i != Regs.end()) { printf("%s\n", (*i).c_str()); i++; } */ } void lowercaseString(string& m) { int x = 0; while(x < m.size()) { m.at(x) = tolower(m.at(x)); x++; } } void stripBlanks(string& buf) { while(!buf.empty() && isspace(buf.at(0))) { buf.erase(0, 1); } while(!buf.empty() && isspace(buf.at(buf.size() - 1))) { buf.erase(buf.size() - 1, 1); } } double delayInCycles() { if(inSeconds) { return(floor(Delay * Clock * 0.25e6 + 0.5)); //Clock * 0.25e6 = instruction cycle frequency, Hz } else { return(floor(Delay + 0.5)); } } double delayInCyclesOld() { if(inSeconds) { return(Delay * Clock * 0.25e6); //Clock * 0.25e6 = instruction cycle frequency, Hz } else { return(Delay); } } int numberOfLoops(double cycles) { int x = 1; if(cycles <= BASICDELAY) { return(0); } while((maximumDelay(x) + BASICDELAY) < cycles && x < MAXNUMBEROFLOOPS) { x++; } return(x); } double maximumDelay(int loops) { int counters[MAXNUMBEROFLOOPS]; int x; for(x = 0; x < loops; x++) { counters[x] = 256; } return(calculateDelay(counters, loops)); } void adjustCounters(int* counters, int loops, double cycles) { int tries = 0; double delay; int x = 0; int offset = 0; double gradient[MAXNUMBEROFLOOPS]; bool large = true; while(tries++ < (256 * loops)) //256 tries for each counter { delay = calculateDelay(counters, loops); findGradient(gradient, counters, loops); //find gradient if(large && (cycles * 0.8) < delay && cycles > delay) { //switch to small steps large = false; counters[x]--; offset = 0; delay = calculateDelay(counters, loops); findGradient(gradient, counters, loops); //find gradient } if(cycles >= delay) { if(large) { //large steps x = findMax(gradient, loops, offset); } else { //small steps x = findMin(gradient, loops, offset); } counters[x]++; if(counters[x] > 256) { counters[x] = 256; if(++offset >= loops) { offset = 0; } } else { offset = 0; } } else { counters[x]--; break; } } } double calculateDelay(int* counters, int loops) { double x; double y; int i; int j; //calculate: //N1 = 3*d1 + 1 //N2 = 3*d1*d2 + 4*d1 + 1 //N3 = 3*d1*d2*d3 + 4*d1*d2 + 4*d1 + 1 //... x = 0; for(i = 0; i < loops; i++) { y = 1; for(j = i - 1; j >= 0; j--) { y *= counters[j]; } x += y * (3 * counters[i] + 1); } return(x); } void findGradient(double* gradient, int* counters, int loops) { double x; double y; int i; int j; int k; bool zero; for(k = 0; k < loops; k++) { //function: //N1 = 3*d1 + 1 //N2 = 3*d1*d2 + 4*d1 + 1 //N3 = 3*d1*d2*d3 + 4*d1*d2 + 4*d1 + 1 //... x = 0; for(i = 0; i < loops; i++) { y = 1; zero = true; for(j = i - 1; j >= 0; j--) { if(j != k) { y *= counters[j]; } else { zero = false; } } if(zero && i != k) { y = 0; } if(i != k) { x += y * (3 * counters[i] + 1); } else { x += 4 * y; } } gradient[k] = x - 1; } } int findMin(double* gradient, int loops, int offset) { int ordered[MAXNUMBEROFLOOPS]; bool passed[MAXNUMBEROFLOOPS]; int x; int y; double min; int m; for(x = 0; x < loops; x++) { passed[x] = false; } for(x = 0; x < loops; x++) { min = maximumDelay(MAXNUMBEROFLOOPS) + 1; m = 0; for(y = 0; y < loops; y++) { if(!passed[y] && gradient[y] <= min) { min = gradient[y]; m = y; } } ordered[x] = m; passed[m] = true; } return(ordered[offset]); } int findMax(double* gradient, int loops, int offset) { int ordered[MAXNUMBEROFLOOPS]; bool passed[MAXNUMBEROFLOOPS]; int x; int y; double max; int m; for(x = 0; x < loops; x++) { passed[x] = false; } for(x = 0; x < loops; x++) { max = 0; m = 0; for(y = 0; y < loops; y++) { if(!passed[y] && gradient[y] >= max) { max = gradient[y]; m = y; } } ordered[x] = m; passed[m] = true; } return(ordered[offset]); } void generateDelay() { double cycles; int loops; int x; char j = '0'; LISTSTR::iterator i; printForm(); //convert to cycles cycles = delayInCycles(); printf("; Actual delay = %.12g seconds = %.12g cycles\n; Error = %.12g %%\n", cycles / Clock / 0.25e6, cycles, (delayInCyclesOld() - cycles) / delayInCyclesOld() * 100); //substract cycles for call+return if(Routine) { cycles -= 4; } //print cblock i = Regs.begin(); loops = numberOfLoops(cycles); if(loops > 0) { printf("\n\tcblock\n"); for(x = 0; x < loops; x++) { printf("\t%s\n", (*i).c_str()); i++; } printf("\tendc\n"); } while(cycles != 0) { cycles -= generateSubDelay(cycles, Name + "_" + j); j++; } if(Routine) { printf("\n\t\t\t;4 cycles (including call)\n\treturn\n"); } } double generateSubDelay(double cycles, string label) { int loops; int counters[MAXNUMBEROFLOOPS]; int x; double d; LISTSTR::iterator i; //find the number of nested loops loops = numberOfLoops(cycles); if(loops != 0) { //Initialize counters for(x = 0; x < loops; x++) { counters[x] = 1; } //adjust coefficients adjustCounters(counters, loops, cycles); d = calculateDelay(counters, loops); printf("\n\t\t\t;%.12g cycles\n", d); i = Regs.begin(); for(x = 0; x < loops; x++) { printf("\tmovlw\t%#.2x\n", counters[x] & 0xFF); printf("\tmovwf\t%s\n", (*i).c_str()); printf("%s%d\n", label.c_str(), x); i++; } for(x = loops - 1; x >= 0; x--) { i--; printf("\tdecfsz\t%s, f\n", (*i).c_str()); printf("\tgoto\t%s%d\n", label.c_str(), x); } return(d); } else { printf("\n\t\t\t;%.12g cycles\n", cycles); x = cycles; while(x != 0) { if(x >= 2) { printf("\tgoto\t$+1\n"); x -= 2; } else { printf("\tnop\n"); x -= 1; } } return(cycles); } }