/* Copyright Motorola, Inc. 1993, 1994 ALL RIGHTS RESERVED You are hereby granted a copyright license to use, modify, and distribute the SOFTWARE so long as this entire notice is retained without alteration in any modified and/or redistributed versions, and that such modified versions are clearly identified as such. No licenses are granted by implication, estoppel or otherwise under any patents or trademarks of Motorola, Inc. The SOFTWARE is provided on an "AS IS" basis and without warranty. To the maximum extent permitted by applicable law, MOTOROLA DISCLAIMS ALL WARRANTIES WHETHER EXPRESS OR IMPLIED, INCLUDING IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE AND ANY WARRANTY AGAINST INFRINGEMENT WITH REGARD TO THE SOFTWARE (INCLUDING ANY MODIFIED VERSIONS THEREOF) AND ANY ACCOMPANYING WRITTEN MATERIALS. To the maximum extent permitted by applicable law, IN NO EVENT SHALL MOTOROLA BE LIABLE FOR ANY DAMAGES WHATSOEVER (INCLUDING WITHOUT LIMITATION, DAMAGES FOR LOSS OF BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, OR OTHER PECUNIARY LOSS) ARISING OF THE USE OR INABILITY TO USE THE SOFTWARE. Motorola assumes no responsibility for the maintenance and support of the SOFTWARE. */ #include "errors.h" #include "dink.h" #include "spr_loc.h" #define TRUE 1 #define FALSE 0 extern dink_loop(); extern clear_index(); extern flush_caches(); extern special_register_read(); extern special_register_write(); extern go_into_guts(); extern flush_breakpoints(); extern restore_breakpoints(); extern breakpoint_set(); extern time_base_init(); extern time_base_read_upper(); extern time_base_read_lower(); extern cache_activate(); extern cache_inhibit(); extern disable_L1_dcache(); extern disable_L1_icache(); extern invalidate_and_enable_L1_icache(); extern invalidate_and_enable_L1_dcache(); extern timebase_register_u; extern timebase_register_l; extern benchmark_in_progress; extern timebase_base_run_l; int bench_mark(long); /* 1 2 3 4 5 6 7 8 01234567890123456789012345678901234567890123456789012345678901234567890123456789 function: bench_mark (address) purpose: This function is designed to easily allow the user to benchmark a piece of stand-alone code. By stand alone we mean that if this piece of code could normally be executed using the 'go' command and it would terminate when an illegal opcode was encountered then this is a good canidate for executing the code with the 'bm' command. The 'bm' command will take 2 passes at running the code at the address listed. The first pass will replace the beginning of the code with an illegal opcode to force an immediate return. That pass will determine the minimum amount of time associated with the overhead of getting out to the code and returning. That value will be subtracted from the amount of time taken to actually execute the code. The counts will be made using the timebase register. This will give the user a value which they will need to convert to seconds. The formula for converting this will involve knowing how many processor clocks is equivalent to a timebase tick and how fast is the processor running. We will endeavour to add this capability into our code as time allows however, it is unlikely that the first attempt will make this calculation. inputs: Three parameters are sent in: address: This is the starting address in memory where we will begin execution. outputs: Printed to screen. return: I will return a SUCCESS if everything went okay and some other inocuous error if something went astray. mod history: 12/31/97 Original version by RGP */ /* 1 2 3 4 5 6 7 8 01234567890123456789012345678901234567890123456789012345678901234567890123456789 */ bench_mark(start_addr) /* Please refer to the header information for descriptions of these parameters. */ long start_addr; { extern int benchmark_pass; /* Global that determines which part of bench_mark routine should be run 0 - Benchmark command just issued 1 - Returning from 1st pass 2 - Returning from 2nd pass */ /* Globals to store timebase value in */ extern timebase_base_run_l; extern timebase_register_u; extern timebase_register_l; extern benchmark_loop; extern benchmark_addr; unsigned int status; int value; /* If benchmark_pass == 0 1. Flush breakpoints 2. Set illegal or breakpoint at start addres (whichever is easier to remove and replace with original opcode 3. Initialize timebase register 4. Read timebase - timebase_0 5. Go into code - launch If benchmark_pass == 1 6. Return thru 0x700 exception handler Modify this handler to look for benchmark in progress to supress exception reporting. If in progress then increment the benchmark_pass global and jump to bench_mark(); 7. Read timebase - timebase_1 8. Report to User that base run is complete and took ___ timebase units 9. Replace illegal opcode at beginning of user code with original value 10. Go into code - launch on pass #2 If benchmark_pass == 2 11. Return thru excpetion handler again 12. Read timebase - timebase_2 13. Report time taken timebase_2 */ /* Add some overhead here to take multiple passes at the code below with varying conditions. The first passes will include: 1. Icache DISABLED Dcache DISABLED 2. Icache ENABLED Dcache DISABLED 3. Icache Disabled Dcache ENABLED 4. Icache ENABLED Dcache ENABLED We will add a global variable benchmark_loop which will track where we are in this sequence. It will be intialized to 0 and be incre- mented during each benchmark loop. A benchmark loop consist of the measurement of the benchmark which in itself will require both of the calls to go_into_guts below. */ /* We will only do this Print statement the first time thru each pass */ if(benchmark_pass <= 0){ switch(benchmark_loop){ case 0: /* Disable both L1 caches */ /* Set flag that a benchmark is in progress (for exception handling) */ benchmark_in_progress = TRUE; benchmark_addr = start_addr; /* Saved for furture passes into benchmark function */ if( breakpoint_set( benchmark_addr ) != SUCCESS ) { PRINT( "Unable to set breakpoint in breakpoint table:\n" ); return (ERROR); } PRINT("Icache OFF Dcache OFF:\n" ); status = special_register_read(HID0_LOC,&value); special_register_write(HID0_LOC,(value & 0xffff33ff)); break; case 1: /* Enable Icache */ PRINT("Icache ON Dcache OFF:\n" ); status = special_register_read(HID0_LOC,&value); value = value & 0xffff33ff; special_register_write(HID0_LOC,(value | 0x00008800)); break; case 2: PRINT("Icache OFF Dcache ON:\n" ); status = special_register_read(HID0_LOC,&value); value = value & 0xffff33ff; special_register_write(HID0_LOC,(value | 0x00004400)); break; case 3: PRINT("Icache ON Dcache ON:\n" ); status = special_register_read(HID0_LOC,&value); value = value & 0xffff33ff; special_register_write(HID0_LOC,(value | 0x0000CC00)); break; case 4: /* We have a little bit of cleanup here */ /* Now I need to do a little cleanup. This will include removing the breakpoint that was set up above. */ clear_index(benchmark_addr); benchmark_in_progress = FALSE; benchmark_loop = 0; /* Reset for next run */ dink_loop(); /* Re-enter DINK !!!! */ break; default: return (ERROR); /* We have a serious problem if we get here */ } /* end switch */ } /* end if */ /* The following code will print 1 line summarizing how long the benchmark from benchmark_addr took. It will perform the math neccessary to determine what the base run to the starting address was and subtract it from the benchmarked time */ if(benchmark_pass == 0) { flush_breakpoints(); /* Push the illegal opcodes out to memory */ flush_caches(); special_register_write(SRR0_LOC,benchmark_addr); /* From here down into code will be identical in both passes of benchmark run */ time_base_init(); go_into_guts(); /* We should be in user code now. We expect to hit the illegal opcode that we set above. */ } else if(benchmark_pass == 1) { /* The timebase register has been stored into the global variable by the exception handler */ timebase_base_run_l = timebase_register_l; restore_breakpoints(); /* Push out real opcodes into memory ... Note: User Code must re-enter DINK thru the illegal opcode handler !!! */ flush_caches(); special_register_write(SRR0_LOC,benchmark_addr); /* From here down into code will be identical in both passes of benchmark run */ time_base_init(); go_into_guts(); /* We should be in user code now. We expect to hit the illegal opcode that we set above. */ } else /* benchmark_pass ==2 */ { /* The timebase register has been stored into the global variable by the exception handler */ status = special_register_read(SRR0_LOC,&value); PRINT( " Benchmark 0x%08lx",benchmark_addr); PRINT( " ---> 0x%08lx:",value); PRINT( " 0x%08lx", timebase_register_u); /* Check for that lame condition where due to the granularity we have come up with a slightly negative number */ if((timebase_register_u == 0) && (timebase_register_l < timebase_base_run_l)) PRINT( " 00000000.\n"); else PRINT( " %08lx\n",timebase_register_l-timebase_base_run_l); /* Reset our flags so we don't continue to think that we are running a benchmark */ benchmark_pass = -1; /* Reset benchmark_pass global */ benchmark_loop++; /* Increment this variable since we have just completed a pass */ go_into_guts(); /* If I go into guts with the SRR0 that represents the address of the end of the benchmark it should be an illegal opcode. This will bring me back into the code above. Somewhat cheesy but hopefully it will work. */ } } /* end bench_mark */