/* ------------------------------------------------------------------------------- * intr_latency.c * * author: Mark McDermott * Created: Jan 30, 2009 * Updated: Apr 23, 2017 For Zedboard * Updated: Feb 2, 2021 For Ultra96 * * This routine measures the average latecncy from the time an interrupt input * to the SOC occurs to when it is handled. This is used to determine how fast * the SOC can handle interrupts from the H1KP * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #undef DEBUG #define DEBUG /* ------------------------------------------------------------------------------- * One-bit masks for bits 0-31 */ #define ONE_BIT_MASK(_bit) (0x00000001 << (_bit)) /* -------------------------------------------------------------------------------*/ #define MAP_SIZE 4096UL #define MAP_MASK (MAP_SIZE - 1) /* ------------------------------------------------------------------------------- * Device path name for the GPIO device */ #define GPIO_DEV_PATH "/dev/gpio_int" #define GPIO_DR_NUM 0x0 // Pin Number #define GPIO_DR 0xA0050004 // Interrupt register // #define GPIO_LED_NUM 0x1 // #define GPIO_LED 0xA0030010 // LED register /* ------------------------------------------------------------------------------- * Number of interrupt latency measurements to take: */ #define NUM_MEASUREMENTS 1000 #define MAX_STRING_LENGTH 32 /* ------------------------------------------------------------------------------- * File descriptor for GPIO device */ int gpio_dev_fd = -1; /* ------------------------------------------------------------------------------- * Counter of number of times sigio_signal_handler() has been executed */ volatile int sigio_signal_count = 0; /* ------------------------------------------------------------------------------- * Flag to indicate that a SIGIO signal has been processed */ static volatile sig_atomic_t sigio_signal_processed = 0; /* ------------------------------------------------------------------------------- * Time stamp set in the last sigio_signal_handler() invocation: */ struct timeval sigio_signal_timestamp; /* ------------------------------------------------------------------------------- * Array of interrupt latency measurements (in micro seconds): */ unsigned long intr_latency_measurements[NUM_MEASUREMENTS]; /* ------------------------------------------------------------------------------- * Function prototypes */ int gpio_set_pin(unsigned int target_addr, unsigned int pin_number, unsigned int bit_val); unsigned long int_sqrt(unsigned long n); void sigio_signal_handler(int signo); void compute_interrupt_latency_stats( unsigned long *min_latency_p, unsigned long *max_latency_p, double *average_latency_p, double *std_deviation_p); int fm(unsigned int target_addr, unsigned int value, unsigned int lp_cnt, unsigned int increment, int fd); /* ------------------------------------------------------------------------------- * Main routine */ int main(int argc, char *argv[]) { volatile int rc; int i; struct timeval start_timestamp; char array[MAX_STRING_LENGTH]; if (argc != 3) { printf("Usage: %s <-s string | -f filename>\n", argv[0]); return EXIT_FAILURE; } if (strcmp(argv[1], "-s") == 0) { strcpy(array, argv[2]); // using array variable to store the string value // processStringInput(argv[2]); //printf("array in -s : %s\n", array); } else if (strcmp(argv[1], "-f") == 0) { const char *filename = argv[2]; printf("File parsing if condition\n"); FILE *file = fopen(filename, "r"); if (file == NULL) { perror("Error opening file"); exit(EXIT_FAILURE); } char array[MAX_STRING_LENGTH]; if (fgets(array, MAX_STRING_LENGTH, file) != NULL) { // Remove trailing newline if present if (array[strlen(array) - 1] == '\n') array[strlen(array) - 1] = '\0'; printf("Content of file '%s': %s\n", filename, array); } else { printf("File '%s' is empty\n", filename); } fclose(file); } else { printf("Invalid option\n"); printf("Usage: %s <-s string | -f filename>\n", argv[0]); return EXIT_FAILURE; } unsigned int hex[MAX_STRING_LENGTH] = {0}; int j; // convertion function from character array to hexadecimal for (i = 0, j = 0; array[i] != '\0'; ++i) { if ((i % 8) == 0 && i != 0) j++; if (array[i] >= '0' && array[i] <= '9') { hex[j] = hex[j] * 16 + (array[i] - '0'); } else if (array[i] >= 'a' && array[i] <= 'f') { hex[j] = hex[j] * 16 + (array[i] - 'a' + 10); } else if (array[i] >= 'A' && array[i] <= 'F') { hex[j] = hex[j] * 16 + (array[i] - 'A' + 10); } else { printf("Invalid character in input string\n"); return; } } // for (int index = 0; index <= j; ++index) // printf("input value : %.8x, %.8x\n", index, hex[index]); /* -------------------------------------------------------------------------- * Register signal handler for SIGIO signal: */ struct sigaction sig_action; memset(&sig_action, 0, sizeof sig_action); sig_action.sa_handler = sigio_signal_handler; /* -------------------------------------------------------------------------- * Block all signals while our signal handler is executing: */ (void)sigfillset(&sig_action.sa_mask); rc = sigaction(SIGIO, &sig_action, NULL); if (rc == -1) { perror("sigaction() failed"); return -1; } /* ------------------------------------------------------------------------- * Open the device file */ gpio_dev_fd = open(GPIO_DEV_PATH, O_RDWR); if (gpio_dev_fd == -1) { perror("open() of " GPIO_DEV_PATH " failed"); return -1; } /* ------------------------------------------------------------------------- * Set our process to receive SIGIO signals from the GPIO device: */ rc = fcntl(gpio_dev_fd, F_SETOWN, getpid()); if (rc == -1) { perror("fcntl() SETOWN failed\n"); return -1; } /* ------------------------------------------------------------------------- * Enable reception of SIGIO signals for the gpio_dev_fd descriptor */ int fd_flags = fcntl(gpio_dev_fd, F_GETFL); rc = fcntl(gpio_dev_fd, F_SETFL, fd_flags | O_ASYNC); if (rc == -1) { perror("fcntl() SETFL failed\n"); return -1; } int dh = open("/dev/mem", O_RDWR | O_SYNC); if (dh == -1) { printf("Unable to open /dev/mem. Ensure it exists (major=1, minor=1)\n"); printf("Must be root to run this routine.\n"); return -1; } volatile unsigned int *APLL_CTRL, *APLL_CFG, *PLL_STATUS, *PL0_REF_CTRL, *PL1_REF_CTRL, *clk_reg; volatile unsigned int *APLL_CTRL_v, *APLL_CFG_v, *PLL_STATUS_v, *PL0_REF_CTRL_v; volatile unsigned int *pl0; // = PL0_REF_CTRL + ((0x00FF5E00C0 & MAP_MASK) >> 2); volatile unsigned int *pl1; // = PL0_REF_CTRL + ((0x00FF5E00C4 & MAP_MASK) >> 2); int num_bytes = 0; // index to have the string length APLL_CTRL = (unsigned int *)mmap(NULL, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, dh, 0x00FD1A0020 & ~MAP_MASK); APLL_CFG = (unsigned int *)mmap(NULL, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, dh, 0x00FD1A0024 & ~MAP_MASK); PLL_STATUS = (unsigned int *)mmap(NULL, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, dh, 0x00FD1A0044 & ~MAP_MASK); PL0_REF_CTRL = (unsigned int *)mmap(NULL, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, dh, 0x00FF5E00C0 & ~MAP_MASK); PL1_REF_CTRL = (unsigned int *)mmap(NULL, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, dh, 0x00FF5E00C4 & ~MAP_MASK); pl0 = PL0_REF_CTRL + ((0x00FF5E00C0 & MAP_MASK) >> 2); pl1 = PL1_REF_CTRL + ((0x00FF5E00C4 & MAP_MASK) >> 2); // APLL_CTRL_v = APLL_CTRL + ((0x00FD1A0020 & MAP_MASK) >> 2); // APLL_CFG_v = APLL_CFG + ((0x00FD1A0024 & MAP_MASK) >> 2); // PLL_STATUS_v = PLL_STATUS + ((0x00FD1A0044 & MAP_MASK) >> 2); // *APLL_CTRL_v = 0x00002D00; // setting to 1500 MHz // *APLL_CFG_v = 0x7E672C6C; // *APLL_CTRL_v += 0x8; // 0x00002D08; // assert bypass // *APLL_CTRL_v += 0x1; // 0x00002D09; // assert reset // *APLL_CTRL_v = *APLL_CTRL_v & 0xFFFFFFFE; // 0x00002D08; // deassert reset // printf("%d, Waiting set\n", *PLL_STATUS_v); // while (*PLL_STATUS_v & 0x1 != 1) // ; // printf("PS clock set\n"); // *APLL_CTRL_v = 0x00002D00; // deassert reset *pl0 = (1 << 24) // bit 24 enables clock | (3 << 16) // bit 23:16 is divisor 1 | (5 << 8); *pl1 = (1 << 24) // bit 24 enables clock | (3 << 16) // bit 23:16 is divisor 1 | (5 << 8); volatile unsigned int *Keccak_CTRL_0, *Keccak_CTRL_0_value; volatile unsigned int *Keccak_num_bytes, *Keccak_num_bytes_value; volatile unsigned int *Keccak_start_address, *Keccak_start_address_value; volatile unsigned int *capture_timer, *capture_timer_value; Keccak_CTRL_0 = (unsigned int *)mmap(NULL, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, dh, 0xb0000000 & ~MAP_MASK); Keccak_num_bytes = (unsigned int *)mmap(NULL, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, dh, 0xb0000004 & ~MAP_MASK); Keccak_start_address = (unsigned int *)mmap(NULL, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, dh, 0xb0000008 & ~MAP_MASK); capture_timer = (unsigned int *)mmap(NULL, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, dh, 0xb0002008 & ~MAP_MASK); Keccak_CTRL_0_value = Keccak_CTRL_0 + ((0xb0000000 & MAP_MASK) >> 2); Keccak_num_bytes_value = Keccak_num_bytes + ((0xb0000004 & MAP_MASK) >> 2); Keccak_start_address_value = Keccak_start_address + ((0xb0000008 & MAP_MASK) >> 2); capture_timer_value = capture_timer + ((0xb0002008 & MAP_MASK) >> 2); /* ------------------------------------------------------------------------- * Take interrupt latency measurements in a loop: */ // fm(0xfffc0000, 0xffffffff, 8, 0, dh); // fm 0xfffc0000 0xffffffff 4 0 num_bytes = 0; int input_num_bytes = 0; int write_memory_location = 0; //while (array[num_bytes] != '\0') // { // num_bytes++; // if (num_bytes % 2 == 0) // input_num_bytes++; // } num_bytes = 48; input_num_bytes = 24; write_memory_location = input_num_bytes / 4; int memory_index = 0; if(input_num_bytes % 8 != 0) { write_memory_location = write_memory_location + 1; } // printf("j: %d\nWrite_memory_location: %d\n", j, write_memory_location); while (write_memory_location > 0) { unsigned int start_address = 0xfffc0000 + (memory_index * 4); // int temp_num_bytes = num_bytes; // printf("Start_address: %.8x, %.8x, %.8x, %.8x\n", start_address, input_num_bytes, (input_num_bytes % 4), hex[memory_index]); if (((input_num_bytes % 4) != 0) && (write_memory_location == 1)) hex[memory_index] = hex[memory_index] << (32 - (input_num_bytes % 4) * 8); fm(start_address, hex[memory_index], 1, 0, dh); // fm 0xfffc0000 0xffffffff 4 0 write_memory_location--; memory_index++; } // printf("Num bytes: %d\nWrite_memory_location: %d\n", input_num_bytes, write_memory_location); // while (1); printf("bytes _cnt: %d\n", input_num_bytes); *Keccak_num_bytes_value = input_num_bytes; *Keccak_start_address_value = 0xfffc0000; // printf("after initialization step\n"); sigset_t signal_mask, signal_mask_old, signal_mask_most; for (i = 0; i < NUM_MEASUREMENTS; i++) { /* --------------------------------------------------------------------- * Reset sigio_signal_processed flag: */ sigio_signal_processed = 0; /* --------------------------------------------------------------------- * NOTE: This next section of code must be excuted each cycle to prevent * a race condition between the SIGIO signal handler and sigsuspend() */ (void)sigfillset(&signal_mask); (void)sigfillset(&signal_mask_most); (void)sigdelset(&signal_mask_most, SIGIO); (void)sigprocmask(SIG_SETMASK, &signal_mask, &signal_mask_old); /* --------------------------------------------------------------------- * Take a start timestamp for interrupt latency measurement */ (void)gettimeofday(&start_timestamp, NULL); /* --------------------------------------------------------------------- * Assert GPIO output pin to trigger generation of edge sensitive interrupt: */ *Keccak_CTRL_0_value = 0x1; // pm 0xb0000000 0x01 - reset keccak *Keccak_CTRL_0_value = 0x0; // pm 0xb0000000 0x00 - no reset keccak *Keccak_CTRL_0_value = 0x4; // pm 0xb0000000 0x00 - no reset keccak *Keccak_CTRL_0_value = 0x0; // pm 0xb0000000 0x00 - no reset keccak *Keccak_CTRL_0_value = 0x02; // pm 0xb0000000 0x02 /* --------------------------------------------------------------------- * Wait for SIGIO signal handler to be executed. */ if (sigio_signal_processed == 0) { rc = sigsuspend(&signal_mask_most); /* Confirm we are coming out of suspend mode correcly */ assert(rc == -1 && errno == EINTR && sigio_signal_processed); } // printf("interrupt detected\n"); (void)sigprocmask(SIG_SETMASK, &signal_mask_old, NULL); assert(sigio_signal_count == i + 1); // Critical assertion!! // printf("timer is negated\n"); unsigned int timer_value_interrupt = *capture_timer_value; *Keccak_CTRL_0_value = 0x0; // pm 0xb0000000 0x00 /* --------------------------------------------------------------------- * Compute interrupt latency: */ //intr_latency_measurements[i] = timer_value_interrupt; /* --------------------------------------------------------------------- * * Compute interrupt latency: * */ intr_latency_measurements[i] = (sigio_signal_timestamp.tv_sec - start_timestamp.tv_sec) * 1000000 + (sigio_signal_timestamp.tv_usec - start_timestamp.tv_usec); } // End of for loop /* ------------------------------------------------------------------------- * Close device file */ (void)close(gpio_dev_fd); /* ------------------------------------------------------------------------- * Compute interrupt latency stats: */ unsigned long min_latency; unsigned long max_latency; double average_latency; double std_deviation; compute_interrupt_latency_stats( &min_latency, &max_latency, &average_latency, &std_deviation); /* * Print interrupt latency stats: */ printf("Minimum Latency: %lu\n" "Maximum Latency: %lu\n" "Average Latency: %f\n" "Standard Deviation: %f\n" "Number of samples: %d\n", min_latency, max_latency, average_latency, std_deviation, NUM_MEASUREMENTS); FILE *fp1; fp1 = fopen("keccak_timer_values.csv", "a"); fprintf(fp1, "%lu, %lu, %f, %f\n", min_latency, max_latency, average_latency, std_deviation); fclose(fp1); return 0; } /* ----------------------------------------------------------------------------- * SIGIO signal handler */ void sigio_signal_handler(int signo) { volatile int rc1; assert(signo == SIGIO); // Confirm correct signal # sigio_signal_count++; // printf("sigio_signal_handler called (signo=%d)\n", signo); /* ------------------------------------------------------------------------- * Set global flag */ sigio_signal_processed = 1; /* ------------------------------------------------------------------------- * Take end timestamp for interrupt latency measurement */ (void)gettimeofday(&sigio_signal_timestamp, NULL); } /* ----------------------------------------------------------------------------- * Compute interrupt latency stats */ void compute_interrupt_latency_stats( unsigned long *min_latency_p, unsigned long *max_latency_p, double *average_latency_p, double *std_deviation_p) { int i; unsigned long val; unsigned long min = ULONG_MAX; unsigned long max = 0; unsigned long sum = 0; unsigned long sum_squares = 0; for (i = 0; i < NUM_MEASUREMENTS; i++) { val = intr_latency_measurements[i]; if (val < min) { min = val; } if (val > max) { max = val; } sum += val; sum_squares += val * val; } *min_latency_p = min; *max_latency_p = max; unsigned long average = (unsigned long)sum / NUM_MEASUREMENTS; unsigned long std_deviation = int_sqrt((sum_squares / NUM_MEASUREMENTS) - (average * average)); *average_latency_p = average; *std_deviation_p = std_deviation; } /* --------------------------------------------------------------- * sqrt routine */ unsigned long int_sqrt(unsigned long n) { unsigned long root = 0; unsigned long bit; unsigned long trial; bit = (n >= 0x10000) ? 1 << 30 : 1 << 14; do { trial = root + bit; if (n >= trial) { n -= trial; root = trial + bit; } root >>= 1; bit >>= 2; } while (bit); return root; } /* ----------------------------------------------------------------------------- * * gpio_set_pin routine: This routine sets and clears a single bit * in a GPIO register. * */ int gpio_set_pin(unsigned int target_addr, unsigned int pin_number, unsigned int bit_val) { unsigned int reg_data; int fd = open("/dev/mem", O_RDWR | O_SYNC); if (fd == -1) { printf("Unable to open /dev/mem. Ensure it exists (major=1, minor=1)\n"); return -1; } volatile unsigned int *regs, *address; regs = (unsigned int *)mmap(NULL, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, target_addr & ~MAP_MASK); address = regs + (((target_addr)&MAP_MASK) >> 2); #ifdef DEBUG1 printf("REGS = 0x%.8x\n", regs); printf("Target Address = 0x%.8x\n", target_addr); printf("Address = 0x%.8x\n", address); // display address value // printf("Mask = 0x%.8x\n", ONE_BIT_MASK(pin_number)); // Display mask value #endif /* Read register value to modify */ reg_data = *address; if (bit_val == 0) { /* Deassert output pin in the target port's DR register*/ reg_data &= ~ONE_BIT_MASK(pin_number); *address = reg_data; } else { /* Assert output pin in the target port's DR register*/ reg_data |= ONE_BIT_MASK(pin_number); *address = reg_data; } int temp = close(fd); if (temp == -1) { printf("Unable to close /dev/mem. Ensure it exists (major=1, minor=1)\n"); return -1; } munmap(NULL, MAP_SIZE); return 0; } int fm(unsigned int target_addr, unsigned int value, unsigned int lp_cnt, unsigned int increment, int fd) { // int fd = open("/dev/mem", O_RDWR | O_SYNC); volatile unsigned int *regs, *address; volatile unsigned int offset; offset = 0; if (fd == -1) { perror("Unable to open /dev/mem. Ensure it exists (major=1, minor=1)\n"); return -1; } if (lp_cnt > 0x3ff) { // Max is 4096 bytes lp_cnt = 0x3ff; printf("Setting max repeat value to 0x3ff\n"); } regs = (unsigned int *)mmap(NULL, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, target_addr & ~MAP_MASK); /* --------------------------------------------------------------- * Main loop */ while (lp_cnt) { address = regs + (((target_addr + offset) & MAP_MASK) >> 2); *address = value; // perform write command // printf("0x%.8x", (target_addr + offset)); // printf(" = 0x%.8x\n", *address); // display register value value = value + increment; // increment value by incr lp_cnt -= 1; // decrement loop offset += 4; // WORD alligned } // End of while loop // int temp = close(fd); // Close memory // if (temp == -1) // { // perror("Unable to close /dev/mem. Ensure it exists (major=1, minor=1)\n"); // return -1; // } // munmap(NULL, MAP_SIZE); // Unmap memory return 0; // Return status } // End of pm routine