/* ----------------------------------------------------- */ These scripts / files have been developed by Praveen Kalla, University of Notre Dame, while working at NEC Laboratories Inc. They are all covered by copyright laws pertaining to both institutions. They can be used for acadameic purposes for free of cost, but not for commercial purposes. The author does not assume any kind of responsibility for any damage through the use of these scripts / programs. Some files might have been modified from their original form as available at SUN Microsystems website. These files would be covered under the copyright laws of SUN Microsystems as well author contact : nkalla@cse.nd.edu (www.nd.edu/~nkalla) advisors : Dr. Sharon Hu (www.nd.edu/~shu) : Dr. Joerg Henkel (henkel@nec-labs.com) Do not remove this information from these files. /* ----------------------------------------------------- */ //-------------------------------------------------------------------------------- // The function of this PLI is as follows: // Specify the "data" and "code" related info in the appropriate // 'define's, that is: start addr for data/code, size of data/code. // The PLI initializes the page tables (for now) and then loads the // "data" part from the data.dat (not implemented yet) and the "code" from // "prog.dat". // The page table data is initialized as follows: // There is only level 1 page table. The context table spans the addresses // 0-255, then the level 1 table starts 256-512. If needed aaditional tables // have to be initialized after this address. The pg_table[0] contains addr // of first data element. The cont_table[0] contains addr of page_table[0]. // The data/code starts from 0x00001000 onwards - first 4KB segment // Addr Val reason // 0 256 pg_table[0] at lev_0 // 1 512 pg_table[1] at lev_0 // . . . // 256 1 top 20 bits of phys addr // 257 2 second 4KB segment // . . // 0x00001000 XX first data element // PROG : info related to "code" part of the program // DATA : info related to "data" part of the program // // rev: Feb 11: The organization of the data files: // addr: 00: 1st line in file: low 32 bits. // : 2nd : high 32 bits // : 08: 3rd : low 32 bits // : 4th : high 32 bits // ... // // The total time taken for a program is being tracked as follows: Two variables // file->start_access , last_access are used to track the first mem_read (starting // instr) and the last mem_read (unimp -> the proc stops after a few cycles, so // error shouldnt be large). the mem dump has to be modified to remove the last set // of ret/restore and unimp n nops should be inserted. // //-------------------------------------------------------------------------------- // // Stall count PLI: // This file also has the function which counts the total stalls . This // count is based on the number of times the Exec unit stalls, which is controlled // by the hold_Mexec1/2/3 signals. // Also the stalls have to be counted only after the main mem has been // accessed for the first time. This happens after 25260 ns (unless u change the // boot sequence - after Oct 23/2001 : This 25260 - Fpu / $ enabled) // // Called in the Exec module //-------------------------------------------------------------------------------- // // Rev Jan 26th 2002: making the mem compliant with the tool // Modified the file so that the memory is initialized in a different way // Nothing else has been changed. The new program reads the combination // (addr, data) // from the file instead of continuos data previously. //-------------------------------------------------------------------------------- // // Rev Jan 29: 2002 : // Changes have been indicated thru the file. //-------------------------------------------------------------------------------- // // Rev Jan 29: 2002 : // changed start addr to 0x00011078 //-------------------------------------------------------------------------------- // Rev Feb 6: 2002 : // added the ability to scan the "stop" address for the program // assumed to be always the addr of the "ret / restore" at the // end of the main program //-------------------------------------------------------------------------------- // Rev Mar 18:2002 : // added the capability to take command line arg for "prog.dat" // otherwise this file has to be present in the dir where the tool // was started. //-------------------------------------------------------------------------------- #include #include #include #include // no data parts for now : Jan 29 / 2002 #define DATA_SIZE 0 #define DATA_START_ADDR 0x00020a90 // Jan 29 : 2002 : program size is quite large. 1529 lines for a very small sample // that includes the support code too. any math libs wud pump it up even more. #define PROG_SIZE 10000 // actually starts at 40001000, but will change boot code to jump to this #define PROG_START_ADDR 0x0011078 #define MEM_SIZE 10000 int initf(); int mem_accessf(); int gen_addrf(); int stall_initf(); int stall_countf(); s_tfcell veriusertfs[] = { {usertask, 0, initf, 0, mem_accessf, 0, "$mem_access" }, {usertask, 0, stall_initf, 0, stall_countf, 0, "$stall_count" }, {usertask, 0, 0, 0, gen_addrf, 0, "$gen_addr" }, {0} }; //-------------------------------------------------------------------------------- // the data structures used to maintain the memory // file : the 'file' that contains the mem data. (not a real file) // dat_strct : addr: addr of the particular data // : val : value of the particular data //-------------------------------------------------------------------------------- typedef struct { int addr; int val_high; int val_low; } dat_struct; typedef struct { int start_access; // used to count the total cycles spent on the program int last_access; // - do - int num; int stop_addr; // the stop address of the main rpog dat_struct *data; } file; dat_struct get_val(file *temp, int addr); int put_val(file *temp, int addr, dat_struct data, int write_case); //-------------------------------------------------------------------------------- // this function initializes the memory. // reads from the file : prog.dat // into the data structure: 'file' // The mem is an array (addr, data) // remaining mem : addr : initialized to -1 // data : NOP (Ox0100000) // PROG_SIZE : number of lines in the prog.dat (each line 8 bytes) // MEM_SIZE : size of total memory //-------------------------------------------------------------------------------- int initf() { FILE *f1, *f2; file *dat; int i, start_addr, addr; char *str, *s_addr, *fprog; long freread; int taddrh, taddrl, tval_high, tval_low; str = (char *)malloc(10*sizeof(char)); s_addr = (char *)malloc(10*sizeof(char)); //this might cause problems in xtra long file paths fprog = (char *)malloc(100*sizeof(char)); fprog = mc_scan_plusargs("datfile"); if(fprog == NULL) { io_printf("\n ERROR: Datfile not specified"); exit(0); } f1 = fopen(fprog,"r+"); f2 = fopen("data.dat","r+"); dat = (file *)malloc(sizeof(file)); dat->start_access = 0; dat->last_access = 0; dat->num = MEM_SIZE; dat->stop_addr = -1; start_addr = PROG_START_ADDR; io_printf("\n Size of mem: %d, start_addr: %x", dat->num, start_addr); dat->data = (dat_struct *)malloc(dat->num*sizeof(dat_struct)); /* there was some dense explanation here which has been moved to the end of the file about endiannness, etc */ /* followed by some code for setting up page tables. since i couldnt get the mmu to work this is no longer interesting, so moved it to the end of file */ // addr & data values get read in by the prog. // the addr: actually something seems to have changd here // the low addr data was being loaded into the data.val_high. // since this has been used before, lets just stick to it. // except for a naming convention, there shudnt be much of a problem // also have to accoutn for the fact that addresses are not continuous i = 0; while(!feof(f1)) { //scanning into temporary locations fscanf(f1, "%8x %8x", &taddrh, &tval_high); fgets(str, 10, f1); // this line might have to be reread if addr are not cont. freread = ftell(f1); if(!feof(f1)) { fscanf(f1, "%8x %8x", &taddrl, &tval_low); fgets(str, 10, f1); } else { taddrl = -1; //exiting } if(taddrl == taddrh + 4) { // no problem dat->data[i].val_high = tval_high; dat->data[i].val_low = tval_low; dat->data[i].addr = taddrh; } else if(taddrl != -1) { // have to load into diff locations // we just go back one line and restart // fixing the current addr dat->data[i].val_high = tval_high; dat->data[i].val_low = 0x01000000; dat->data[i].addr = taddrh; // starting a new 2-line block read // going back to the freread pos fseek(f1, freread, SEEK_SET); } else { dat->data[i].val_high = tval_high; dat->data[i].val_low = 0x01000000; dat->data[i].addr = taddrh; } // io_printf(" %x %x @ 0x%x", dat->data[i].val_high, dat->data[i].val_low, dat->data[i].addr); i++; if ( i > PROG_SIZE) { io_printf("\n PROGRAM TOO LARGE! INCREASE SPACE IN PLI"); exit(0); } } /* addr = DATA_START_ADDR; for(i=PROG_SIZE; idata[i].val_high); fscanf(f2, "%x", &dat->data[i].val_low); dat->data[i].addr = addr; addr += 8; } */ //initializing the rest of the mem: addr = -1. i--; while(i < MEM_SIZE) { dat->data[i].addr = -1; dat->data[i].val_high = 0x01000000; dat->data[i].val_low = 0x01000000; i++; } // getting the stop addr from the command line s_addr = mc_scan_plusargs("s_addr"); if(s_addr != NULL) { sscanf(s_addr, "%x", &dat->stop_addr); io_printf("\n stop_addr: %x", dat->stop_addr); } else { io_printf("\n WARNING: Stop addr not specified"); } tf_setworkarea((char *)dat); fclose(f1); io_printf("\n Init done"); } //-------------------------------------------------------------------------------- // this function does the mem access. both read/write // arguments: addr, data, op_code // addr: the addr at which some op has to be performed // data: the corresponding data val (32 bits, remove comments // and make changes for 64) // op_code: read - 0 // write - 1 // outputs: data if opcode = 0 else none //-------------------------------------------------------------------------------- int mem_accessf() { int curr_time; file *temp; int i, temp_val, addr, read_op, high_word_write, low_word_write, double_word_write; int write_case; dat_struct data; char *temp_str, *word_write; handle addr_handle, data_handle, oe_handle, we_handle, cas_l_1_handle, cas_l_0_handle; s_acc_value *addr_struct; s_acc_value *data_struct; s_setval_delay data_delay; acc_initialize(); acc_configure(accDevelopmentVersion, "IEEE 1364 PLI"); (char *) temp = tf_getworkarea(); //getting handles addr_handle = acc_handle_tfarg(1); data_handle = acc_handle_tfarg(2); oe_handle = acc_handle_tfarg(3); //read - 0 we_handle = acc_handle_tfarg(4); //write - 0 // distinguishing betwn the high n low word accesses - for writes - assuming all // reads are double word reads - shouldnt be much of a problem // cas_l[0] : 0 : low word // cas_l[1] : 1 : high word // both : 0 : then a double word write. cas_l_1_handle = acc_handle_tfarg(5); cas_l_0_handle = acc_handle_tfarg(6); //allocing mem addr_struct = (s_acc_value *) malloc(sizeof(s_acc_value)); addr_struct->value.vector = (p_acc_vecval ) malloc(sizeof(s_acc_vecval)); addr_struct->format = accVectorVal; data_struct = (s_acc_value *) malloc(sizeof(s_acc_value)); data_struct->value.vector = (p_acc_vecval ) malloc(2*sizeof(s_acc_vecval)); data_struct->format = accVectorVal; //setting the delay if data needs to be op data_delay.model = accNoDelay; data_delay.time.type = accTime; data_delay.time.low = 0; data_delay.time.high = 0; //getting the read/write info read_op = 0; temp_str = acc_fetch_value(oe_handle, "%d", NULL); temp_val = atoi(temp_str); if(temp_val == 0) { read_op = 1; // mem access for a read } // double check temp_str = acc_fetch_value(we_handle, "%d", NULL); temp_val = atoi(temp_str); if(temp_val == 0) { if(read_op == 1) { io_printf("\n Error: read n write access"); } read_op = 0; // mem access for a read } // getting info for a write access (the high.low word) high_word_write = 0; word_write = acc_fetch_value(cas_l_0_handle, "%d", NULL); temp_val = atoi(word_write); if(temp_val == 0) { high_word_write = 1; // high word write } low_word_write = 0; word_write = acc_fetch_value(cas_l_1_handle, "%d", NULL); temp_val = atoi(word_write); if(temp_val == 0) { low_word_write = 1; // low word write } if(high_word_write == 1 && low_word_write == 1) { double_word_write = 1; } acc_fetch_value(addr_handle, "%%", addr_struct); if(read_op == 0) { acc_fetch_value(data_handle, "%%", data_struct); } curr_time = tf_gettime(); if(read_op == 1) { if(temp->start_access == 0) { temp->start_access = curr_time; } if(temp->last_access < curr_time) { temp->last_access = curr_time; } // io_printf("\n start: %d, last: %d", temp->start_access, temp->last_access); // io_printf("\n elapsed cycles: %d\n", (temp->last_access - temp->start_access ) / 20); //read access.... if(addr_struct->value.vector->bval == 0) { //just a check addr = addr_struct->value.vector->aval; //data at addr -- low 32 data = get_val(temp, addr); data_struct->value.vector[0].bval = 0; data_struct->value.vector[0].aval = data.val_low; // -- high 32 data_struct->value.vector[1].bval = 0; data_struct->value.vector[1].aval = data.val_high; io_printf("\n addr: %x, val_h : %x%x \n", addr, data.val_high, data.val_low); } else { //addr invalid. so putting out X data_struct->value.vector[0].bval = 1; data_struct->value.vector[0].aval = 1; data_struct->value.vector[1].bval = 1; data_struct->value.vector[1].aval = 1; } acc_set_value(data_handle, data_struct, &data_delay); } else { //write access.... if(addr_struct->value.vector->bval == 0) { //just a check //addr valid addr = addr_struct->value.vector->aval; if((data_struct->value.vector[0].bval == 0) && (data_struct->value.vector[1].bval == 0)) { //data valid for low bits & high bits data.val_low = data_struct->value.vector[0].aval; data.val_high = data_struct->value.vector[1].aval; if(double_word_write == 1) { write_case = 3; // io_printf("\n Double word write \n"); } else if( high_word_write == 1) { write_case = 2; // io_printf("\n High word write \n"); } else if( low_word_write == 1) { write_case = 1; // io_printf("\n Low word write \n"); } i= put_val(temp, addr, data, write_case); // io_printf("\n check :"); // io_printf("write : val: %x \n***####*****\n", temp->data[i].val_low); } } } acc_close(); } //-------------------------------------------------------------------------------- // used for reading mem // this fn: inputs: address; file; // : outs : data@address_in_file //-------------------------------------------------------------------------------- dat_struct get_val(file *temp, int addr) { dat_struct data; int i; if((addr+4 == temp->stop_addr) || (addr-4 == temp->stop_addr) || (addr == temp->stop_addr)) { // time to stop // only one probem : if addr gets accessed due to $ read, tschh!! tf_dostop(); io_printf("\n Program expected to complete by now. If it hasnt, continue the \n simulation for 1-2 us. \n An easy identification of program completion is an access\n to memory locations starting from 0"); } for(i=0; inum;i++) { if(temp->data[i].addr == addr) { data.val_low = temp->data[i].val_low; data.val_high = temp->data[i].val_high; return(data); } else if(temp->data[i].addr == -1) { // io_printf("\n Error \n"); temp->data[i].addr = addr; data.val_low = temp->data[i].val_low; data.val_high = temp->data[i].val_high; return(data); } } } //-------------------------------------------------------------------------------- // used for writing mem // this fn: inputs: address; data@address; file; //-------------------------------------------------------------------------------- int put_val(file *temp, int addr, dat_struct val, int write_case) { int i; io_printf("\n write: addr: %x, val %x%x\n", addr, val.val_high, val.val_low); for(i=0;inum;i++) { if(temp->data[i].addr == addr) { // io_printf("\n before write : val: %x", temp->data[i].val_low); // io_printf("\n changed ..."); if(write_case == 3) { temp->data[i].val_low = val.val_low; temp->data[i].val_high = val.val_high; } else if(write_case == 2) { temp->data[i].val_high = val.val_high; // low : will be garbage if no previous data exists } else if(write_case == 1) { temp->data[i].val_low = val.val_low; // high : will be garbage if no previous data exists } // io_printf("\n after write : val: %x", temp->data[i].val_low); return i; } else if(temp->data[i].addr == -1) { // io_printf("\n before write : val: %x", temp->data[i].val); temp->data[i].addr = addr; if(write_case == 3) { temp->data[i].val_low = val.val_low; temp->data[i].val_high = val.val_high; } else if(write_case == 2) { temp->data[i].val_high = val.val_high; // low : will be garbage if no previous data exists } else if(write_case == 1) { temp->data[i].val_low = val.val_low; // high : will be garbage if no previous data exists } // io_printf("\n after write : val: %x", temp->data[i].val); return i; } } } //-------------------------------------------------------------------------------- // this function generates random addresses. // used initially for checking. not used now. //-------------------------------------------------------------------------------- int gen_addrf() { handle addr_handle; s_setval_value *addr_struct; s_setval_delay addr_delay; int addr, quo, rem, i; acc_initialize(); acc_configure(accDevelopmentVersion, "IEEE 1364 PLI"); addr_handle = acc_handle_tfarg(1); addr = PROG_START_ADDR + 4*(random() % PROG_SIZE); // io_printf("\n addr: (dec) %d", addr); addr_struct = (s_setval_value *) malloc(sizeof(s_setval_value)); addr_struct->value.vector = (p_acc_vecval ) malloc(sizeof(s_acc_vecval)); //setting the data addr_struct->format = accVectorVal; addr_struct->value.vector->bval = 0; addr_struct->value.vector->aval = addr; //setting the delay addr_delay.model = accNoDelay; addr_delay.time.type = accTime; addr_delay.time.low = 0; addr_delay.time.high = 0; acc_set_value(addr_handle, addr_struct, &addr_delay); acc_close(); } //-------------------------------------------------------------------------------- // The following code is for the stall_count //-------------------------------------------------------------------------------- typedef struct { int stalls; //used by the stall_count_pli int start_time; // time after which the stalls have to be counted } stall_data_struct; //-------------------------------------------------------------------------------- // this function initializes the workarea //-------------------------------------------------------------------------------- int stall_initf() { stall_data_struct *data; char *start_str; data = (stall_data_struct *)malloc(sizeof(stall_data_struct)); data->stalls = 0; data->start_time = 0; tf_setworkarea((char *)data); } //-------------------------------------------------------------------------------- // This function counts the stalls. // FPU / $ enabled : after 25260 ns // FPU en / $ dis : // FPU dis / $ dis : //-------------------------------------------------------------------------------- int stall_countf() { int curr_time; stall_data_struct *temp; int temp_val; char *temp_str; handle hold_handle; acc_initialize(); acc_configure(accDevelopmentVersion, "IEEE 1364 PLI"); (char *) temp = tf_getworkarea(); //getting handles hold_handle = acc_handle_tfarg(1); // getting value temp_str = acc_fetch_value(hold_handle, "%d", NULL); temp_val = atoi(temp_str); // checking time & updating curr_time = tf_gettime(); if(curr_time > 25260 ) { if(temp_val == 1) { temp->stalls++; io_printf("\n Stall_count: %d \n", temp->stalls); } } else temp->stalls = 0; } //these are all big endian //Refers to which bytes are most significant in multi-byte data types. In big-endian architectures, // the leftmost bytes (those with a lower address) are most significant. In little-endian architectures, // the rightmost bytes are most significant. For example, consider the number 1025 (2 to the tenth // power plus one) stored in a 4-byte integer /* // 00000000 00000000 00000100 00000001 // Address Big-Endian representation of 1025 Little-Endian representation of 1025 00 01 02 03 00000000 00000000 00000100 00000001 00000001 00000100 00000000 00000000 */ /* switching off the MMU for now ... //putting in the page tables (has to be automated later if reqd) // curently can handle prog+data of 1MB dat->data[0].addr = 0; dat->data[0].val = 256; dat->data[1].addr = 8; dat->data[1].val = 512; dat->data[2].addr = 256; dat->data[2].val = 1; dat->data[3].addr = 257; dat->data[3].val = 2; - refer to page table sizes and so on in the user manual.... */