// ======================================================================================================== // ======================================================================================================== // ***************************************** device_provision.c ******************************************* // ======================================================================================================== // ======================================================================================================== #include "common.h" #include "device_common.h" //#include "device_regen_funcs.h" #define TIMING_VAL_DIVISOR 0 // ======================================================================================================== // ======================================================================================================== // Send the timing data to the verifier through the socket. THIS IS ONLY ALLOWED during enrollment. void SendTimings(int max_string_len, int verifier_socket_desc, int vec_num, int num_timing_vals, int *overall_num_timing_vals_ptr, unsigned short **timing_val_arr, unsigned short **output_pos, unsigned short **MPS_settings, int num_sams, int change_rise_fall, int include_MPS) { char timing_line[max_string_len]; char buffer[max_string_len]; int sam_num, nz_num; int num_lines; unsigned short MPS_setting1, MPS_setting2; int got_2nd_MPS_setting; // Load up the timing samples to a string and send to the verifier. num_lines = 0; for ( nz_num = 0; nz_num < num_timing_vals; nz_num++ ) { if ( include_MPS == 1 ) { // Include MPS setting. MPS_setting1 = MPS_setting2 = MPS_settings[nz_num][0]; got_2nd_MPS_setting = 0; for ( sam_num = 0; sam_num < num_sams; sam_num++ ) if ( MPS_setting1 != MPS_settings[nz_num][sam_num] ) { if ( got_2nd_MPS_setting == 0 ) { MPS_setting2 = MPS_settings[nz_num][sam_num]; got_2nd_MPS_setting = 1; } // If we found a 3rd MPS_setting, then this is an error condition. else if ( MPS_setting1 != MPS_settings[nz_num][sam_num] && MPS_setting2 != MPS_settings[nz_num][sam_num] ) { printf("ERROR: SendTimings(): Found a 3rd MPS setting %d, %d and %d!\n", MPS_setting1, MPS_setting2, MPS_settings[nz_num][sam_num]); fflush(stdout); } } sprintf(timing_line, "V: %d\tO: %d\tC: %d\tMPS1: %d\tMPS2: %d\t", vec_num, output_pos[nz_num][0], *overall_num_timing_vals_ptr, MPS_setting1, MPS_setting2); } else sprintf(timing_line, "V: %d\tO: %d\tC: %d\t", vec_num, output_pos[nz_num][0], *overall_num_timing_vals_ptr); for ( sam_num = 0; sam_num < num_sams; sam_num++ ) { sprintf(buffer, " %d", timing_val_arr[nz_num][sam_num]); strcat(timing_line, buffer); // Sanity check: It must always be true that the sampled output remains the same across samples. if ( output_pos[nz_num][0] != output_pos[nz_num][sam_num] ) { printf("ERROR: SendTimings(): Output that produced timing value has CHANGED %d vs %d!\n", output_pos[nz_num][0], output_pos[nz_num][sam_num]); fflush(stdout); exit(EXIT_FAILURE); } } // Send timing value line with all samples included. Be sure to add '+1' to ensure NULL character is transferred to receiver. if ( SockSendB((unsigned char *)timing_line, strlen(timing_line) + 1, verifier_socket_desc) < 0 ) { printf("ERROR: SendTimings(): Send '%s' failed\n", timing_line); fflush(stdout); exit(EXIT_FAILURE); } // DEBUG //printf("SendTimings(): Current timing line[%d]:\n'%s'\n", *overall_num_timing_vals_ptr, timing_line); fflush(stdout); num_lines++; (*overall_num_timing_vals_ptr)++; } // Send the empty string which will cause a blank line to be inserted into the saved file on the receiver, as an indicator of when we change // from rising to falling (or vise versa). Be sure to add '+1' to ensure NULL character is transferred to receiver. if ( change_rise_fall == 1 ) if ( SockSendB((unsigned char *)"", strlen("") + 1, verifier_socket_desc) < 0 ) { printf("ERROR: SendTimings(): Send '' failed\n"); fflush(stdout); exit(EXIT_FAILURE); } // DEBUG #ifdef DEBUG printf("SendTimings(): Sent %d timing values for vector[%d]\n", nz_num, vec_num); fflush(stdout); #endif } // ======================================================================================================== // ======================================================================================================== // Start the PUF engine and get the calibration values. char cal_string[100*MAX_STRING_LEN]; int GenGetCalVals(int max_string_len, volatile unsigned int *CtrlRegA, volatile unsigned int *DataRegA, int ctrl_mask, int verifier_socket_desc) { int num_challenges, max_vals, ARB_length, MPS_tap_points, MPS_sum_counts, MPS_num_averages, MPS_num_results; int load_or_unload, byte_or_word_data; int i, j, scaler, MPS_num; char temp_str[max_string_len]; // This depends on the length of the carry chain AND TIMING_VAL_DIVISOR. ONLY used for sanity check below. int MAX_CAL_VAL = 127; num_challenges = 16; ARB_length = 64; MPS_tap_points = 40; MPS_sum_counts = 2*(MPS_tap_points - 1); MPS_num_averages = MPS_tap_points - 1; MPS_num_results = MPS_tap_points - 1; max_vals = (MPS_tap_points * ARB_length)*num_challenges + MPS_sum_counts + MPS_num_averages + MPS_num_results; scaler = 16; signed short *cal_vals; if ( (cal_vals = (signed short *)calloc(max_vals, sizeof(signed short))) == NULL ) { printf("ERROR: GenGetCalVals(): Failed to allocate storage for %d cal_vals!\n", max_vals); fflush(stdout); } // printf("GenGetCalVals(): Info: Allocated %d 'signed short' on stack!\n", max_vals); fflush(stdout); // MEMORY LAYOUT: // 2 rows of timing values, each num_challenges * ARB_length TP_MAX_ITERATIONS = (`TP_SEL_NB * `TP_NC_NB), e.g., Arb length (64) * Number of challenges (16). // A set of sum-counts of size 2*(MPS_top_points -1) = 2 * (MPS_SEL_NB -1) = 2 * (number of MPS - 1) (38) // A set of averages of size (MPS_top_points -1) = (MPS_SEL_NB -1) = (number of MPS - 1) (19) // A set of results of size (MPS_top_points -1) = (MPS_SEL_NB -1) = (number of MPS - 1) (19) if ( 2 * ARB_length * num_challenges + 4 * (MPS_num_averages - 1) > 8192 ) { printf("WARNING: Number of values stored on FPGA %d > BRAM memory allocated on FPGA 8192!\n", 2 * ARB_length * num_challenges); fflush(stdout); } // Initialize in case we overrun our memory and don't write to some values. for ( i = 0; i < max_vals; i++ ) cal_vals[i] = 0; printf("GenGetCalVals(): NumChal %d\tARB Len %d\tMPS_TPs %d\tMPS_sum %d\tMPS_num_aves %d\tMPS_num_res %d\n", num_challenges, ARB_length, MPS_tap_points, MPS_sum_counts, MPS_num_averages, MPS_num_results); fflush(stdout); // Sanity check. PUF engine MUST be 'ready' if ( (*DataRegA & (1 << IN_SM_READY)) == 0 ) { printf("ERROR: GenGetCalVals(): PUF Engine is NOT ready!\n"); fflush(stdout); exit(EXIT_FAILURE); } //printf("GenGetCalVals(): PUF/hash engine is ready -- starting PUF engine!\n"); fflush(stdout); // Start the PUF engine to generate the timing values. Vector pair has already been loaded. Added delay here because // this should be a handshake signal and it is not. When the PL side clock is really slow (5 MHz), its possible the // start signal is asserted here in the C program and then deasserted BEFORE the PL side sees it. This created a // nasty bug for me when I started testing the loops and had the clock set at 6.25MHz in the PL side. *CtrlRegA = ctrl_mask | (1 << OUT_CP_PUF_START); usleep(1000); *CtrlRegA = ctrl_mask; printf("GenGetCalVals(): ctrl_mask %x!\n", ctrl_mask); fflush(stdout); // Check here for errors before starting LoadUnloadBRAM int locked_up = 0; while ( ((*DataRegA) & (1 << IN_SM_HANDSHAKE)) == 0 ) { locked_up++; if ( locked_up > 10000000 ) { printf("ERROR: GenGetCalVals(): 'stopped' has not been asserted for the threshold number of cycles -- Locked UP?\n"); fflush(stdout); // Print the status of the Calibration error flag. if ( ((*DataRegA) & (1 << IN_CAL_ERR)) != 0 ) { printf("GenGetCalVals(): CAL ERROR!\n"); exit(EXIT_FAILURE); } locked_up = 0; } } // Changed the calibration routine to do pairs of rows at a time. In order to get the raw data, I call the CAL_DEBUG data dump after each // row of data is generated (except the first row). As each row (MPS_setting) is produced, I find the number of valid column pairings of // this new row with the previous row. I subtract only the valid pairing to produce a sum and num_vals_in_sum. I then print out both rows + // the entire data set associated with the sums and counts, e.g., ALL MPS sums and counts are printed even though some have NOT been filled // in yet because we have not tested that MPS setting and collected timing data. So I need to do this LoadUnloadBRAM (MPS_settings-1) times. int num_vals; num_vals = 2 * num_challenges * ARB_length + MPS_sum_counts; load_or_unload = 1; byte_or_word_data = 1; for ( MPS_num = 0; MPS_num < MPS_tap_points - 1; MPS_num++ ) { // CREATE A STRING TO SEND TO SERVER. Clear string strcpy(cal_string, ""); for ( i = 0; i < max_vals; i++ ) cal_vals[i] = 0; #ifdef DEBUG #endif // This routine gets the data into a unsigned byte character array. The second argument needs to be twice the number of 16-bit words. LoadUnloadBRAM(max_string_len, num_vals, CtrlRegA, DataRegA, ctrl_mask, NULL, cal_vals, load_or_unload, byte_or_word_data, 0); sprintf(temp_str, "MPS %d\n\tFirst Row:\t", MPS_num); printf("%s", temp_str); strcat(cal_string, temp_str); for ( i = 0; i < num_challenges * ARB_length; i++ ) { sprintf(temp_str, "%3d ", cal_vals[i]); printf("%s", temp_str); strcat(cal_string, temp_str); } sprintf(temp_str, "\n\tSecond Row:\t"); printf("%s", temp_str); strcat(cal_string, temp_str); for ( i = num_challenges * ARB_length; i < 2*(num_challenges * ARB_length); i++ ) { sprintf(temp_str, "%3d ", cal_vals[i]); printf("%s", temp_str); strcat(cal_string, temp_str); } printf("\n"); strcat(cal_string, "\n"); // Do a sanity check on the numbers. Compute the sum over both rows of data. int k, row_sum = 0, row_cnt = 0; for ( k = 0; k < num_challenges * ARB_length; k++ ) if ( cal_vals[k] != 0 && cal_vals[k] != MAX_CAL_VAL && cal_vals[k + num_challenges * ARB_length] != 0 && cal_vals[k + num_challenges * ARB_length] != MAX_CAL_VAL && cal_vals[k] >= cal_vals[k + num_challenges * ARB_length] ) { row_sum += (cal_vals[k] - cal_vals[k + num_challenges * ARB_length]); row_cnt++; } if ( row_sum != cal_vals[2*(num_challenges * ARB_length) + 2*MPS_num] ) { printf("ERROR: GenGetCalVals(): Row Sum %d does NOT agree with value computed by hardware %d!\n", row_sum, cal_vals[2*(num_challenges * ARB_length) + 2*MPS_num]); fflush(stdout); } if ( row_cnt != cal_vals[2*(num_challenges * ARB_length) + 2*MPS_num + 1] ) { printf("ERROR: GenGetCalVals(): Row Cnt %d does NOT agree with value computed by hardware %d!\n", row_cnt, cal_vals[2*(num_challenges * ARB_length) + 2*MPS_num + 1]); fflush(stdout); } // Note that the GPIO interface/VHDL code dumps all sum/cnts for every row pairing. As mentioned above, the hardware dumps sums and counts for ALL MPS // settings even though some are NOT filled in yet. The 2*MPS_num prints ONLY the pairing of values computed for the current row of timing values that // was just fetched. We need to keep an eye on the counts field -- make sure it does not go to 0. #ifdef DEBUG printf("SUM/CNTS\n"); for ( i = 2*(num_challenges * ARB_length); i < 2*(num_challenges * ARB_length) + 2*(MPS_tap_points-1); i += 2 ) printf("%2d\t%4d\t%2d\n", i/2, cal_vals[i], cal_vals[i + 1]); #endif sprintf(temp_str, "\tSum/Cnt:\t%4d/%2d\n", cal_vals[2*(num_challenges * ARB_length) + 2*MPS_num], cal_vals[2*(num_challenges * ARB_length) + 2*MPS_num + 1]); printf("%s", temp_str); strcat(cal_string, temp_str); if ( SockSendB((unsigned char *)cal_string, strlen(cal_string) + 1, verifier_socket_desc) < 0 ) { printf("ERROR: Send 'cal_string' failed\n"); fflush(stdout); exit(EXIT_FAILURE); } // On the last iteration, print them all just to be sure my addressing is correct. if ( MPS_num == MPS_tap_points - 2 ) { strcpy(cal_string, ""); sprintf(temp_str, "\tSum/Cnt:\n"); printf("%s", temp_str); strcat(cal_string, temp_str); for ( i = 2*(num_challenges * ARB_length), j = 0; i < 2*(num_challenges * ARB_length) + MPS_sum_counts; i += 2, j++ ) { sprintf(temp_str, "\tPair %2d\t%4d/%2d\n", j, cal_vals[i], cal_vals[i+1]); printf("%s", temp_str); strcat(cal_string, temp_str); } if ( SockSendB((unsigned char *)cal_string, strlen(cal_string) + 1, verifier_socket_desc) < 0 ) { printf("ERROR: Send 'cal_string' failed\n"); fflush(stdout); exit(EXIT_FAILURE); } } } for ( i = 0; i < max_vals; i++ ) cal_vals[i] = 0; #ifdef DEBUG #endif // Get the remaining MPS_num_averages + MPS_num_results values at the end. num_vals = MPS_num_averages + MPS_num_results; LoadUnloadBRAM(max_string_len, num_vals, CtrlRegA, DataRegA, ctrl_mask, NULL, cal_vals, load_or_unload, byte_or_word_data, 0); strcpy(cal_string, ""); sprintf(temp_str, "Ave: "); printf("%s", temp_str); strcat(cal_string, temp_str); for ( i = 0; i < MPS_num_averages; i++ ) { sprintf(temp_str, "%8.4f\t", (float)cal_vals[i]/scaler); printf("%s", temp_str); strcat(cal_string, temp_str); } sprintf(temp_str, "\n"); printf("%s", temp_str); strcat(cal_string, temp_str); sprintf(temp_str, "Res: "); printf("%s", temp_str); strcat(cal_string, temp_str); for ( ; i < MPS_num_averages + MPS_num_results; i++ ) { sprintf(temp_str, "%8.4f\t", (float)cal_vals[i]/scaler); printf("%s", temp_str); strcat(cal_string, temp_str); } sprintf(temp_str, "\n"); printf("%s", temp_str); strcat(cal_string, temp_str); if ( SockSendB((unsigned char *)cal_string, strlen(cal_string) + 1, verifier_socket_desc) < 0 ) { printf("ERROR: Send 'cal_string' failed\n"); fflush(stdout); exit(EXIT_FAILURE); } // Free up allocated storage. if (cal_vals != NULL) free(cal_vals); return 0; } // ======================================================================================================== // ======================================================================================================== // Start the PUF engine with the current vector to get the timing values. Once it finishes, fetch each of // the timing values. int GenGetTimingVals(volatile unsigned int *CtrlRegA, volatile unsigned int *DataRegA, int num_POs, unsigned short **timing_val_arr, unsigned short **output_pos, unsigned short **MPS_settings, int sam_num, int ctrl_mask) { int output_num, timing_val; int num_non_zero_timing_vals; int MPS_setting; //printf("GenGetTimingVals_TDC(): BEGIN\n"); fflush(stdout); // Sanity check. PUF engine should be 'ready' but we carry out calibration first. if ( (*DataRegA & (1 << IN_SM_READY)) == 0 ) { printf("ERROR: GenGetTimingVals_TDC(): PUF Engine is NOT ready!\n"); fflush(stdout); exit(EXIT_FAILURE); } //printf("GenGetTimingVals_TDC(): PUF/hash engine is ready -- starting PUF engine!\n"); fflush(stdout); // Start the PUF engine to generate the timing values. Vector pair has already been loaded. Added delay here because // this should be a handshake signal and it is not. When the PL side clock is really slow (5 MHz), its possible the // start signal is asserted here in the C program and then deasserted BEFORE the PL side sees it. This created a // nasty bug for me when I started testing the loops and had the clock set at 6.25MHz in the PL side. // Start the PUF engine to generate the timing values. Vector pair has already been loaded. *CtrlRegA = ctrl_mask | (1 << OUT_CP_PUF_START); usleep(1000); *CtrlRegA = ctrl_mask; // Wait for ready to go low while ( (*DataRegA & (1 << IN_SM_READY)) != 0 ); // ================== // In the TDC version, we fetch timing values as they become available. num_non_zero_timing_vals = 0; timing_val = 0; output_num = 0; MPS_setting = 0; while (1) { // Check if VHDL indicates a timing value is ready. if ( ((*DataRegA) & (1 << IN_SM_HANDSHAKE)) != 0 ) { // Read a timing value -- only 12 bits of the 16. timing_val = (*DataRegA) & 0x00000FFF; // #define IN_FU_OUTPUT_END 20 // #define IN_FU_OUTPUT_START 12 // Get the functional output number that was tested bits 17-12 (6 bits for upto 64 outputs). output_num = (((*DataRegA) & 0x0003F000) >> 12); //#define IN_MPS_3 24 //#define IN_LOAD_VEC_PAIR_OR_MPS_2 23 //#define IN_DONE_ALL_VECS_OR_MPS_1 22 //#define IN_BG_SBS_DONE_OR_MPS_0 21 // DEBUG ONLY. MPS setting. Only the low order 4 bits are available. MPS_setting = (((*DataRegA) & 0x01E00000) >> 21); timing_val_arr[num_non_zero_timing_vals][sam_num] = (unsigned short)timing_val; output_pos[num_non_zero_timing_vals][sam_num] = (unsigned short)output_num; MPS_settings[num_non_zero_timing_vals][sam_num] = (unsigned short)MPS_setting; #ifdef DEBUG printf("GenGetTimingVals_TDC(): Timing val at index %d and sam %d is %d with output pos %d\n", num_non_zero_timing_vals, sam_num, timing_val_arr[num_non_zero_timing_vals][sam_num], output_pos[num_non_zero_timing_vals][sam_num]); fflush(stdout); #endif if ( ((*DataRegA) & (1 << IN_CAL_ERR)) != 0 ) { printf("GenGetTimingVals_TDC(): CAL ERROR!\n"); exit(EXIT_FAILURE); } if ( ((*DataRegA) & (1 << IN_PNDIFF_OVERFLOW_ERR)) != 0 ) { printf("GenGetTimingVals_TDC(): PN(DIFF)_OVERFLOW ERROR for timing val at index %d and sam %d is %d with output pos %d and MPS setting %d\n", num_non_zero_timing_vals, sam_num, timing_val_arr[num_non_zero_timing_vals][sam_num], output_pos[num_non_zero_timing_vals][sam_num], MPS_setting); exit(EXIT_FAILURE); } if ( ((*DataRegA) & (1 << IN_GPEVCAL_ERR)) != 0 ) { printf("GenGetTimingVals_TDC(): GPEVCAL ERROR for timing val at sam index %d and sam %d is %d with output pos %d and MPS setting %d\n", num_non_zero_timing_vals, sam_num, timing_val_arr[num_non_zero_timing_vals][sam_num], output_pos[num_non_zero_timing_vals][sam_num], MPS_setting); exit(EXIT_FAILURE); } num_non_zero_timing_vals++; // Sanity check if ( num_non_zero_timing_vals > num_POs ) { printf("ERROR: GenGetTimingVals_TDC(): Too many timing values %d -- max %d -- increase in program!\n", num_non_zero_timing_vals, num_POs); exit(EXIT_FAILURE); } // Complete the handshake *CtrlRegA = ctrl_mask | (1 << OUT_CP_HANDSHAKE); while ( ((*DataRegA) & (1 << IN_SM_HANDSHAKE)) != 0 ); *CtrlRegA = ctrl_mask; if ( timing_val == 0 ) { printf("ERROR: GenGetTimingVals_TDC(): timing value is 0 for position %d!\n", output_num); exit(EXIT_FAILURE); } if ( output_num >= num_POs ) { printf("ERROR: GenGetTimingVals_TDC(): functional output position %d larger than max %d!\n", output_num, num_POs); exit(EXIT_FAILURE); } } // If the PUF engine finishes, then there are no more timing values available for this vector/sam_num. if ( (*DataRegA & (1 << IN_SM_READY)) != 0 ) break; } return num_non_zero_timing_vals; } // ======================================================================================================== // ======================================================================================================== // ======================================================================================================== // ======================================================================================================== char position_str[MAX_STRING_LEN]; char version_str[MAX_STRING_LEN]; int main(int argc, char *argv[]) { volatile unsigned int *CtrlRegA; volatile unsigned int *DataRegA; char verifier_IP[MAX_STRING_LEN]; int verifier_socket_desc = 0; int port_number; char chip_name[MAX_STRING_LEN]; char outfile_prefix[MAX_STRING_LEN]; unsigned char **challenges_b; unsigned char **masks_b = NULL; unsigned short **MPS_settings; char bitstream_name[MAX_STRING_LEN]; char program_command[MAX_STRING_LEN]; int num_chlngs, num_sams; int chlng_num, sam_num; //Timing variables. #if DEBUG struct timeval t0, t1; long elapsed; #endif int overall_num_timing_vals, num_rise_chlngs; int first_num_non_zero_timing_vals, num_non_zero_timing_vals; int ctrl_mask; int has_masks; char fu_command_string[MAX_STRING_LEN]; char *char_ptr; int num_chlng_bits; int num_POs; // Timing values and position per challenge. unsigned short **timing_val_arr; unsigned short **output_pos; int include_MPS; int program_PL; // ====================================================================================================================== // COMMAND LINE if ( argc != 5 ) { printf("ERROR: device_provision.elf(): Chip name (C1) -- verifier_IP -- port (8888) -- Program PL (0/1)\n"); return(1); } sscanf(argv[1], "%s", chip_name); strcpy(verifier_IP, argv[2]); sscanf(argv[3], "%d", &port_number); sscanf(argv[4], "%d", &program_PL); // ====================================================== PARAMETERS ==================================================== // To enable parallel runs on the server. // port_number = 8888; num_sams = 16; num_chlng_bits = NUM_CHLNG_BITS; num_POs = NUM_POS; include_MPS = 0; // ====================================================================================================================== // Open up the memory mapped device so we can access the GPIO registers. int fd = open("/dev/mem", O_RDWR|O_SYNC); if (fd < 0) { printf("ERROR: /dev/mem could NOT be opened!\n"); exit(EXIT_FAILURE); } // Add 2 for the DataReg (for an offset of 8 bytes for 32-bit integer variables). NOTE: FPGA MAY NOT BE PROGRAMMED so do NOT reference these yet. DataRegA = mmap(0, getpagesize(), PROT_READ|PROT_WRITE, MAP_SHARED, fd, GPIO_0_BASE_ADDR); CtrlRegA = DataRegA + 2; // Open up a socket connection to the verifier. OpenSocketClient returns -1 on failure. if ( OpenSocketClient(MAX_STRING_LEN, verifier_IP, port_number, &verifier_socket_desc) < 0 ) { printf("ERROR: Open socket call to server failed!\n"); exit(EXIT_FAILURE); } // Only need to be big enough to hold the max number of timing values per vector in this version. if ( (timing_val_arr = (unsigned short **)malloc(num_POs * sizeof(unsigned short *))) == NULL ) { printf("ERROR: No storage available for timing_val array!\n"); exit(EXIT_FAILURE); } if ( (output_pos = (unsigned short **)malloc(num_POs * sizeof(unsigned short *))) == NULL ) { printf("ERROR: No storage available for output_pos array!\n"); exit(EXIT_FAILURE); } if ( (MPS_settings = (unsigned short **)malloc(num_POs * sizeof(unsigned short *))) == NULL ) { printf("ERROR: No storage available for MPS_settings array!\n"); exit(EXIT_FAILURE); } int PO_num; for ( PO_num = 0; PO_num < num_POs; PO_num++ ) { if ( (timing_val_arr[PO_num] = (unsigned short *)malloc(num_sams * sizeof(unsigned short))) == NULL ) { printf("ERROR: No storage available for timing_val array!\n"); exit(EXIT_FAILURE); } if ( (output_pos[PO_num] = (unsigned short *)malloc(num_sams * sizeof(unsigned short))) == NULL ) { printf("ERROR: No storage available for output_pos array!\n"); exit(EXIT_FAILURE); } if ( (MPS_settings[PO_num] = (unsigned short *)malloc(num_sams * sizeof(unsigned short))) == NULL ) { printf("ERROR: No storage available for MPS_settings array!\n"); exit(EXIT_FAILURE); } } // ================== // ================== // Verifier is controlling labview now. Loop until server sends the 'EXIT' string. strcpy(fu_command_string, ""); while (1) { // Get the FU to see if there is another iteration to carry out. if ( SockGetB((unsigned char *)fu_command_string, MAX_STRING_LEN, verifier_socket_desc) < 0 ) { printf("ERROR: Failed to receive FU/Command string!\n"); exit(EXIT_FAILURE); } // ------------------------------------------ // Check for EXIT command from the verifier. if ( strcmp(fu_command_string, "EXIT") == 0 ) { printf("RECEIVED 'EXIT' string!\n"); fflush(stdout); break; } // VERSION: Parse out the version number 1 in, e.g., "..._V4". ASSUMES WE HAVE P1 to P9 (NOT P10, etc). char_ptr = strstr(fu_command_string, "_V"); // Sanity check if ( char_ptr == NULL ) { printf("ERROR: Failed to find '_V' in fu_command_string '%s'!\n", fu_command_string); exit(EXIT_FAILURE); } // Skip '_' char_ptr++; version_str[0] = *char_ptr; char_ptr++; version_str[1] = *char_ptr; version_str[2] = '\0'; // POSITION: Parse out the position number 1 in, e.g., "..._P1". ASSUMES WE HAVE P1 to P9 (NOT P10, etc). char_ptr = strstr(fu_command_string, "_P"); // Sanity check if ( char_ptr == NULL ) { printf("ERROR: Failed to find '_P' in fu_command_string '%s'!\n", fu_command_string); exit(EXIT_FAILURE); } // Skip '_' char_ptr++; position_str[0] = *char_ptr; char_ptr++; position_str[1] = *char_ptr; position_str[2] = '\0'; sprintf(bitstream_name, "SR_RFM_%s_TDC_Macro_%s.bit.bin", version_str, position_str); printf("Got version '%s' and position '%s' from command string %s\n\t\tConstructed bitstream name '%s'\tProgramming FPGA...\n", version_str, position_str, fu_command_string, bitstream_name); fflush(stdout); // Program device. sprintf(program_command, "echo %s > /sys/class/fpga_manager/fpga0/firmware", bitstream_name); // 12_26_2021: Had trouble with this in the past where I was getting different results if I programmed the PL side. Making it // conditional. NOTE: YOU MUST RUN verifier_provision with ONLY one 'num_positions' and YOU MUST MANUALLY program the PL side. if ( program_PL == 1 ) { printf("\nPROGRAMMING FPGA with command '%s'\n\n", program_command); system(program_command); } // Set the control mask to indicate provisioning. ctrl_mask = (1 << OUT_CP_MODE1) | (1 << OUT_CP_MODE0); // ------------------------------------------ // Read all the challenge from the verifier into a set of malloc'ed string arrays. Verifier will send number of rising // challenge (inspects challenge as it reads them) and indicate whether masks will also be sent. num_chlngs = ReceiveChlngsAndMasks(MAX_STRING_LEN, verifier_socket_desc, &challenges_b, num_chlng_bits, &num_rise_chlngs, &has_masks, num_POs, &masks_b); printf("Number of challenges received %d\tNumber of rising challenges %d\tHas masks ? %d\n", num_chlngs, num_rise_chlngs, has_masks); fflush(stdout); // DEBUG // Regenerate the challenges in an ASCII file so that we can verify that they are the same ones that were generated. #ifdef DEBUG SaveASCIIChlngs(MAX_STRING_LEN, num_chlngs, challenges_b, num_chlng_bits, has_masks, num_POs, masks_b); #endif // Be sure to add '+1' to ensure NULL character is transferred to receiver. sprintf(outfile_prefix, "%s_%s", chip_name, fu_command_string); if ( SockSendB((unsigned char *)outfile_prefix, strlen(outfile_prefix) + 1, verifier_socket_desc) < 0 ) { printf("ERROR: Send '%s' failed\n", outfile_prefix); exit(EXIT_FAILURE); } // Run time information #ifdef DEBUG gettimeofday(&t0, 0); #endif // Do a hardware RESET. Added delay here because this should be a handshake signal and it is not. *CtrlRegA = (1 << OUT_CP_RESET) | TIMING_VAL_DIVISOR; usleep (1000); *CtrlRegA = TIMING_VAL_DIVISOR; usleep (1000); *CtrlRegA = 0; usleep (10000); // Repeat for n challenges. NOTE: Enrollment mode of operation allows the PUF to be run on a per-challenges basis, for as many samples as you // want (CollectPNs.vhd is NOT RUN -- ONLY LCDT). This iterative version allows very large sets of timing values to be retrieved since // the timing values for each challenges (ALL SAMPLES) are sent after they are generated. overall_num_timing_vals = 0; for ( chlng_num = 0; chlng_num < num_chlngs; chlng_num++ ) { // Progress indicator if ( (chlng_num + 1) % 100 == 0 ) { printf("Processing challenge number %d\n", chlng_num); fflush(stdout); } // Transfer a challenge to the VHDL registers. LoadChlngAndMask(MAX_STRING_LEN, CtrlRegA, DataRegA, chlng_num, challenges_b, ctrl_mask, num_chlng_bits, CHLNG_CHUNK_SIZE, has_masks, num_POs, masks_b); // DEBUG: Re-initialize array for ( PO_num = 0; PO_num < num_POs; PO_num++ ) for ( sam_num = 0; sam_num < num_sams; sam_num++ ) timing_val_arr[PO_num][sam_num] = 0.0; // Get timing values over n samples. for ( sam_num = 0; sam_num < num_sams; sam_num++ ) { #ifdef DEBUG printf("Chlng %d\tSam %d\n", chlng_num, sam_num); fflush(stdout); #endif // Run calibration only on the first vector and first sample. 'GenGetCalVals' checks if PUF engine is ready, starts the engine, // waits then retrieves the calibration data from the PUF. if ( chlng_num == 0 && sam_num == 0 ) { #ifdef DEBUG printf("Running calibration on first vec, first sam\n"); fflush(stdout); #endif ctrl_mask = ctrl_mask | (1 << OUT_CP_DO_CALIBRATION); GenGetCalVals(MAX_STRING_LEN, CtrlRegA, DataRegA, ctrl_mask, verifier_socket_desc); ctrl_mask = ctrl_mask & ~(1 << OUT_CP_DO_CALIBRATION); #ifdef DEBUG printf("AFTER calibration: ctrl_mask %x!\n", ctrl_mask); fflush(stdout); #endif *CtrlRegA = ctrl_mask; } // 'GenGetTimingVals' checks if PUF engine is ready, starts the engine, waits for strobing to complete and then retrieves the timing // values for all outputs under this challenge (number of timing value lines to be transmitted to the server is given by // 'num_non_zero_timing_vals' num_non_zero_timing_vals = GenGetTimingVals(CtrlRegA, DataRegA, num_POs, timing_val_arr, output_pos, MPS_settings, sam_num, ctrl_mask); // Sanity check. Same paths must be timed across ALL n samples. if ( sam_num == 0 ) first_num_non_zero_timing_vals = num_non_zero_timing_vals; else if ( first_num_non_zero_timing_vals != num_non_zero_timing_vals ) { printf("ERROR: Number of paths timed for first sample is NOT equal for other samples!\n"); exit(EXIT_FAILURE); } } // Transmit the timing values for this challenge (including all samples) to the verifier one line at a time. Last parameter sends the empty string to // the verifier after this last rising edge challenge is applied, which adds a extra line in the output file between rising and falling data values. SendTimings(MAX_STRING_LEN, verifier_socket_desc, chlng_num, num_non_zero_timing_vals, &overall_num_timing_vals, timing_val_arr, output_pos, MPS_settings, num_sams, (chlng_num == num_rise_chlngs - 1), include_MPS); // To allow multiple boards and parallelism, we send a 'CONT' string after the entire dataset (all samples) are collected for a given challenge // EXCEPT FOR THE LAST VECTOR. The 'DONE' string is sent instead. if ( chlng_num < num_chlngs - 1 && SockSendB((unsigned char *)"CONT", strlen("CONT") + 1, verifier_socket_desc) < 0 ) { printf("ERROR: Send 'CONT' failed\n"); exit(EXIT_FAILURE); } } // Send "DONE" string to the data for ALL challenge is complete. Be sure to add '+1' to ensure NULL character is transferred to receiver. if ( SockSendB((unsigned char *)"DONE", strlen("DONE") + 1, verifier_socket_desc) < 0 ) { printf("ERROR: Send 'DONE' failed\n"); exit(EXIT_FAILURE); } // Free up the challenge malloced space for next run. for ( chlng_num = 0; chlng_num < num_chlngs; chlng_num++ ) { if ( challenges_b[chlng_num] != NULL ) free(challenges_b[chlng_num]); challenges_b[chlng_num] = NULL; if ( has_masks == 1 ) { if ( masks_b[chlng_num] != NULL ) free(masks_b[chlng_num]); masks_b[chlng_num] = NULL; } } free(challenges_b); if ( has_masks == 1 ) free(masks_b); } // Run time information #ifdef DEBUG gettimeofday(&t1, 0); elapsed = (t1.tv_sec-t0.tv_sec)*1000000 + t1.tv_usec-t0.tv_usec; printf("\tDone: Elapsed %ld us\n\n", (long)elapsed); #endif close(verifier_socket_desc); return 0; }