// ======================================================================================================== // ======================================================================================================== // ***************************************** verifier_provision.c ***************************************** // ======================================================================================================== // ======================================================================================================== #include "common.h" #include "verifier_common.h" // ======================================================================================================== // ======================================================================================================== // Receive timing values and write them to an outfile. This routine receives the set of output lines for each // challenge, one at a time, to allow parallelism (the data from multiple devices to be collected simultaneousl). // The device sends 'CONT' when it finishes sending all output lines for a given challenge, and 'DONE' when the // whole process is complete. #define RECEIVE_BUF_SIZE (100*MAX_STRING_LEN) char buffer[RECEIVE_BUF_SIZE]; int ReceiveTimingVals(int max_string_len, int device_client_socket_desc, FILE **OUTFILE_ptr) { // Receive timing values and write them into the *OUTFILE_ptr. while (1) { if ( SockGetB((unsigned char *)buffer, RECEIVE_BUF_SIZE, device_client_socket_desc) < 0 ) { printf("ERROR: ReceiveTimingVals(): Error receiving timing value line\n"); exit(EXIT_FAILURE); } // One the last transmission of a timing value line, the device sends the 'DONE' string INSTEAD OF THE 'CONT' string. if ( strcmp(buffer, "DONE") == 0 ) { // Write a final in output file. fprintf(*OUTFILE_ptr, "\n"); break; } // The device sends a 'CONT' string when all the timing values lines (with each line containing all samples) is complete for the // current challenge. Break in this case so we can collect data in parallel with multiple devices inserted into the temp. chamber. if ( strcmp(buffer, "CONT") == 0 ) break; fprintf(*OUTFILE_ptr, "%s\n", buffer); } if ( strcmp(buffer, "DONE") == 0 ) return 1; return 0; } // ======================================================================================================== // ======================================================================================================== // ======================================================================================================== // ======================================================================================================== char chlng_file_path[MAX_STRING_LEN]; char mask_file_path[MAX_STRING_LEN]; char FU_COMMAND_PREFIX[MAX_STRING_LEN]; char position_str[MAX_STRING_LEN]; char labview_server_IP[MAX_STRING_LEN]; char slave0_server_IP[MAX_STRING_LEN]; char slave1_server_IP[MAX_STRING_LEN]; char outfile_name_midfix[MAX_STRING_LEN]; char outfile_name_suffix[MAX_STRING_LEN]; char outfile_name[MAX_STRING_LEN]; char temp_str[MAX_STRING_LEN]; char chlng_dir[MAX_STRING_LEN]; char chlng_file_prefix[MAX_STRING_LEN]; char OUTFILE_NAME_PREFIX[MAX_STRING_LEN]; char fu_command_string[MAX_STRING_LEN]; int main(int argc , char *argv[]) { int chlng_num, num_chlngs, num_rise_chlngs; int chlng_set; unsigned char **challenges_b; unsigned char **masks_b = NULL; int has_masks; int labview_server_socket_desc = 0; int labview_client_socket_desc = 0; struct sockaddr_in labview_addr; int labview_port_number; int slave0_server_socket_desc = 0; int slave0_client_socket_desc = 0; struct sockaddr_in slave0_addr; int slave0_port_number; int slave1_server_socket_desc = 0; int slave1_client_socket_desc = 0; struct sockaddr_in slave1_addr; int slave1_port_number; // Extended industrial range int max_TVs = 19; int temperatures[19] = {25, 25, 25, 25, 0, 0, 0, -35, -35, -35, -40, -40, -40, 85, 85, 85, 100, 100, 100}; float voltages[19] = {1.00, 0.95, 1.00, 1.05, 0.95, 1.00, 1.05, 0.95, 1.00, 1.05, 0.95, 1.00, 1.05, 0.95, 1.00, 1.05, 0.95, 1.00, 1.05}; float currents_uAs[19] = {-40e-6, -35e-6, -40e-6, -45e-6, -35e-6, -40e-6, -45e-6, -35e-6, -40e-6, -45e-6, -35e-6, -40e-6, -45e-6, -35e-6, -40e-6, -45e-6, -35e-6, -40e-6, -45e-6}; // float currents_uAs[19] = {-80e-6, -60e-6, -80e-6, -100e-6, -60e-6, -80e-6, -100e-6, -60e-6, -80e-6, -100e-6, -60e-6, -80e-6, -100e-6, -60e-6, -80e-6, -100e-6, -60e-6, -80e-6, -100e-6}; int TV_num, start_TV, end_TV; int labview_present; int position_num, num_positions; int num_chlng_bits; int num_POs; int num_direction_chlng_bits; // int num_permutations_per_chlng; struct timeval t0, t1; long elapsed; int num_slaves; int do_slave0, do_slave1; int slave0_done, slave1_done; int do_2nd_SMU; FILE *OUTFILE0, *OUTFILE1; // =============================================================================== // COMMAND LINE. if ( argc != 11 ) { printf("ERROR: verifier_provision : labview IP (127.0.0.1) -- slave0 server IP (192.168.1.20/xxx.xxx.x.x to disable) -- port0 (8888) -- slave1 server IP (192.168.2.20/xxx.xxx.x.x) -- has masks (0/1) -- start TV (0) -- end TV (19) -- labview present (0/1) -- Chlng set (1-5) -- bitstream (0-3)\n\n"); exit(EXIT_FAILURE); } strcpy(labview_server_IP, argv[1]); strcpy(slave0_server_IP, argv[2]); sscanf(argv[3], "%d", &slave0_port_number); strcpy(slave1_server_IP, argv[4]); sscanf(argv[5], "%d", &has_masks); sscanf(argv[6], "%d", &start_TV); sscanf(argv[7], "%d", &end_TV); sscanf(argv[8], "%d", &labview_present); sscanf(argv[9], "%d", &chlng_set); sscanf(argv[10], "%d", &position_num); // Issue warning if position_num is specified if ( position_num != -1 ) printf("WARNING: With position_num specified (not -1), be sure to program FPGA manually and use '0' for device_provision parameter\n"); // ============================================ PARAMETERS ===================================================== // Number of permutations of MUX bits/chlng. Sanity check below -- PROBABLY WILL DROP THIS b/c it forces all permutations // to be tested // num_permutations_per_chlng = 24; // Test case has a 2X4 array of modules, where each module has one row of SRs and two rows of RFMs, each with 8 bits each. // Number of high order direction bits, one bit per row in the SRP num_direction_chlng_bits = NUM_DIRECTION_CHLNG_BITS; // Number of challenge bits for the test case is 8 modules * 32-bits/module + 16 direction bits, for a total of 272 bits. num_chlng_bits = NUM_CHLNG_BITS; num_POs = NUM_POS; // Test cases // GenerateVectors writes out 24 chlngs per challenge. NOTE: With 1 challenge, we ONLY get 24 FALLING chlngs if ( chlng_set == 1 ) { strcpy(chlng_dir, "../../CHALLENGE_GEN/Systematic/"); strcpy(chlng_file_prefix, "SR_RFM_V4_MMCM_24Chlngs_768DPDs"); } // Here we get 48 chlngs, SR LUT addr bits are all 0, 24 rise and 24 fall else if ( chlng_set == 2 ) { strcpy(chlng_dir, "../../CHALLENGE_GEN/Systematic/"); strcpy(chlng_file_prefix, "SR_RFM_V4_MMCM_48Chlngs_1536DPDs"); } // Here we get 768 chlngs, 384 are rise, 384 are fall. The SR LUT addr bits are rotated through all combinations of 0 to 15, // with 24 rise and fall chlngs for each addr bit combination. Note that the addr bit rotation start over for falling chlngs. else if ( chlng_set == 3 ) { strcpy(chlng_dir, "../../CHALLENGE_GEN/Systematic/"); strcpy(chlng_file_prefix, "SR_RFM_V4_MMCM_768Chlngs_24576DPDs"); } // PathAnalysis, find_dominant, update_master_stats + analyze_master_stats results else if ( chlng_set == 4 ) { strcpy(chlng_dir, "./"); strcpy(chlng_file_prefix, "SR_RFM_V4_MMCM_Random_Rise_1000Vs_Fall_1000Vs_NumSeeds_10"); } // Test of masks using TVCharacterization vectors. else if ( chlng_set == 5 ) { strcpy(chlng_dir, "../../../../ZED/SR_RFM/ANALYSIS/HardwareExps/"); strcpy(chlng_file_prefix, "optKEK_TVN_0.70_WID_1.20_SetSize_64000"); has_masks = 1; } else { printf("ERROR: Challenge file %d with requested number of challenges does not exist!\n", chlng_set); exit(EXIT_FAILURE); } strcpy(FU_COMMAND_PREFIX, "SR_RFM_V4_TDC"); // strcpy(OUTFILE_NAME_PREFIX, "../../ANALYSIS/data/10_5_2021/"); // strcpy(OUTFILE_NAME_PREFIX, "../../ANALYSIS/data/12_25_2021/"); // 10_29_2022: USED THIS FOR A WHILE, but then re-synthesized because I added ScalingConstant to verilog. // strcpy(OUTFILE_NAME_PREFIX, "../../ANALYSIS/data/12_29_2021/"); strcpy(OUTFILE_NAME_PREFIX, "../ANALYSIS/data/2_15_2023/"); if ( position_num >= 0 && position_num <= 3 ) num_positions = position_num + 1; else { printf("ERROR: PUF position number %d MUST be between 0 and 3!\n", position_num); exit(EXIT_FAILURE); } // DO NOT ALLOW auto-config mode. Use Vivado or PCAP to program device for each PUF position. // else // { // position_num = 0; // num_positions = 4; // } // ============================================================================================================= // TEMPERATURE/VOLTAGE EXPERIMENTS ONLY WITH LABVIEW ONLY // If running both boards // Set the max_TVs, temperatures, voltages and currents_uAs above for the number of TV corners to test at. // BE CAREFUL HERE -- IF YOU INDICATE THAT A SLAVE IS PRESENT, THEN BE SURE IT IS INSERTED IN TEMP. CHAMBER AND PLUGGED IN // OTHERWISE THESE CURRENTS WILL BE OUT OF RANGE, and ammeter will go into compliance mode (which forces the voltage to // be 0.61 V (hopefully it is set to this)). if ( strcmp(slave0_server_IP, "xxx.xxx.x.x") == 0 ) do_slave0 = 0; else do_slave0 = 1; if ( strcmp(slave1_server_IP, "xxx.xxx.x.x") == 0 ) { do_slave1 = 0; do_2nd_SMU = 0; } // If only one board at a time, then assume Keithley source meter for setting device core voltage is NOT present. I don't have // Labview GPIB-USB working with Labview 2020 and Redhat 7.0 -- in fact, this NI GPIB interface is NOT supported by NI. I have // a GPIB-PCI but am using it for the O-Scope experiments. Need to order another one. else { do_slave1 = 1; do_2nd_SMU = 1; } // Labview and device port numbers labview_port_number = 8887; // slave0_port_number = 8888; slave1_port_number = 8888; // ============================================================================================================= // For each enabled slave, add 1 to the number of slaves. num_slaves = 0; if ( do_slave0 == 1 ) num_slaves++; if ( do_slave1 == 1 ) num_slaves++; // Sanity check if ( num_slaves == 0 ) { printf("ERROR: Number of slaves MUST be at least 1 %d\n", num_slaves); exit(EXIT_FAILURE); } // Check input parameters if ( start_TV < 0 || end_TV >= max_TVs ) { printf("ERROR: start and end TV MUST be bound between 0 and MAX-1 -> %d\n", max_TVs - 1); exit(EXIT_FAILURE); } // DO NOT FORGET TO DISABLE THE FIREWALL on line. Open the sockets to labview and the device(s) and begin listening. Next to last param is 0 to // indicate that we want to do MORE than just 'accept' a connection. if ( labview_present == 1 ) { printf("\nWaiting for incoming connections from labview for Provisioning...\n\n"); fflush(stdout); OpenSocketServer(MAX_STRING_LEN, &labview_server_socket_desc, labview_server_IP, labview_port_number, &labview_client_socket_desc, &labview_addr, 0, 0); printf("Connected labview to listening port\n"); } if ( do_slave0 == 1 ) { printf("\nWaiting for incoming connections from First device for Provisioning at address %s...\n\n", slave0_server_IP); fflush(stdout); OpenSocketServer(MAX_STRING_LEN, &slave0_server_socket_desc, slave0_server_IP, slave0_port_number, &slave0_client_socket_desc, &slave0_addr, 0, 0); printf("Connected first device to listening port\n"); } if ( do_slave1 == 1 ) { printf("\nWaiting for incoming connections from Second device for Provisioning at address %s...\n\n", slave1_server_IP); fflush(stdout); OpenSocketServer(MAX_STRING_LEN, &slave1_server_socket_desc, slave1_server_IP, slave1_port_number, &slave1_client_socket_desc, &slave1_addr, 0, 0); printf("Connected second device to listening port\n"); } // ============================================================================================================ // TV LOOP // ============================================================================================================ // Repeat the analysis across n TV corners. for ( TV_num = start_TV; TV_num <= end_TV; TV_num++ ) { gettimeofday(&t0, 0); // Send labview commands if ( labview_present == 1 ) SetTVCorner(MAX_STRING_LEN, TV_num, max_TVs, temperatures, currents_uAs, labview_client_socket_desc, do_2nd_SMU); //close(labview_server_socket_desc); //exit(EXIT_SUCCESS); // --------------------------------------- // Iterate over all versions (placements) of the functional unit. for ( ; position_num < num_positions; position_num++ ) { // Add the version component of the bitstream name sprintf(fu_command_string, "%s_P%d", FU_COMMAND_PREFIX, position_num+1); // Print bitstream suffix printf("\nBITSTREAM SUFFIX '%s'\n\n", fu_command_string); fflush(stdout); // Send the FU/command string to device. if ( do_slave0 == 1 ) if ( SockSendB((unsigned char *)fu_command_string, strlen(fu_command_string) + 1, slave0_client_socket_desc) < 0 ) { printf("ERROR: Send '%s' failed\n", fu_command_string); exit(EXIT_FAILURE); } if ( do_slave1 == 1 ) if ( SockSendB((unsigned char *)fu_command_string, strlen(fu_command_string) + 1, slave1_client_socket_desc) < 0 ) { printf("ERROR: Send '%s' failed\n", fu_command_string); exit(EXIT_FAILURE); } // Read challenges and masks. sprintf(mask_file_path, "%s/%s_masks.txt", chlng_dir, chlng_file_prefix); sprintf(chlng_file_path, "%s/%s_vecs.txt", chlng_dir, chlng_file_prefix); printf("Read challenge file '%s'\n", chlng_file_path); fflush(stdout); #ifdef DEBUG #endif num_chlngs = ReadChlngAndMaskFilesBinary(MAX_STRING_LEN, chlng_file_path, num_chlng_bits, &num_rise_chlngs, &challenges_b, has_masks, mask_file_path, num_POs, &masks_b, num_direction_chlng_bits); // Sanity check. Eventually WILL BE DROPPED. // if ( chlng_set * num_permutations_per_chlng != num_chlngs ) // { // printf("ERROR: Requested number of challenges * permutations/chlng %d not equal to number read for file %d\n", // chlng_set * num_permutations_per_chlng, num_chlngs); exit(EXIT_FAILURE); // } printf("\tNumber of chlngs %d\tNumber of rise chlngs %d\n", num_chlngs, num_rise_chlngs); fflush(stdout); #ifdef DEBUG #endif // Send chlngs and masks to the device. if ( do_slave0 == 1 ) SendChlngsAndMasks(MAX_STRING_LEN, num_chlngs, slave0_client_socket_desc, num_rise_chlngs, num_chlng_bits, challenges_b, has_masks, num_POs, masks_b); if ( do_slave1 == 1 ) SendChlngsAndMasks(MAX_STRING_LEN, num_chlngs, slave1_client_socket_desc, num_rise_chlngs, num_chlng_bits, challenges_b, has_masks, num_POs, masks_b); // Construct outfile name suffix. Distinguish between enroll and regeneration TV corners. sprintf(outfile_name_suffix, "_%dC_%.2fV_NCs_%d", temperatures[TV_num], voltages[TV_num], num_chlngs); if ( TV_num == 0 ) strcat(outfile_name_suffix, "_E"); else strcat(outfile_name_suffix, "_R"); strcat(outfile_name_suffix, "_PUFNums.txt"); // Receive timing values from device write to an output file. The device sends a 'CONT' string when all the timing values lines (with each line containing all // samples) is complete for the current challenge and ReceiveTimingVals returns 0. When the all timing values for last challenge are transmitted by the device, the // device sends 'DONE', which causes ReceiveTimingVals to return 1. This allows us to collect data in parallel with multiple device. // Disable the slave0 by default. Open up file if slave0 is enabled strcpy(temp_str, ""); slave0_done = 1; if ( do_slave0 == 1 ) { slave0_done = 0; // Get output midfix name, which includes the chip name ('prefix' is the data directory name set above). if ( SockGetB((unsigned char *)outfile_name_midfix, MAX_STRING_LEN, slave0_client_socket_desc) < 0 ) { printf("ERROR: Error receiving outfile_name_midfix '%s'\n", outfile_name_midfix); exit(EXIT_FAILURE); } sprintf(outfile_name, "%s%s%s", OUTFILE_NAME_PREFIX, outfile_name_midfix, outfile_name_suffix); printf("Saving values to '%s'\n", outfile_name); fflush(stdout); if ( (OUTFILE0 = fopen(outfile_name, "w")) == NULL ) { printf("ERROR: Could NOT open output filename '%s'\n", outfile_name); exit(EXIT_FAILURE); } strcpy(temp_str, outfile_name_midfix); } // Disable the slave1 by default. Open up file if slave1 is enabled slave1_done = 1; if ( do_slave1 == 1 ) { slave1_done = 0; if ( SockGetB((unsigned char *)outfile_name_midfix, MAX_STRING_LEN, slave1_client_socket_desc) < 0 ) { printf("ERROR: Error receiving outfile_name_midfix '%s'\n", outfile_name_midfix); exit(EXIT_FAILURE); } sprintf(outfile_name, "%s%s%s", OUTFILE_NAME_PREFIX, outfile_name_midfix, outfile_name_suffix); printf("Saving values to '%s'\n", outfile_name); fflush(stdout); if ( (OUTFILE1 = fopen(outfile_name, "w")) == NULL ) { printf("ERROR: Could NOT open output filename '%s'\n", outfile_name); exit(EXIT_FAILURE); } // Sanity check -- don't allow both devices to use the same filename if ( strlen(temp_str) != 0 && strcmp(temp_str, outfile_name_midfix) == 0 ) { printf("ERROR: BOTH TOKENS USE THE SAME CHIP NAME '%s'\n", outfile_name_midfix); exit(EXIT_FAILURE); } } // Loop until BOTH the devices are done (both 'done' variables become 1). This allows us to collect data, chlng-by-chlng, in parallel // among multiple devices. while ( slave0_done == 0 || slave1_done == 0 ) { if ( slave0_done == 0 ) slave0_done = ReceiveTimingVals(MAX_STRING_LEN, slave0_client_socket_desc, &OUTFILE0); if ( slave1_done == 0 ) slave1_done = ReceiveTimingVals(MAX_STRING_LEN, slave1_client_socket_desc, &OUTFILE1); } if ( do_slave0 == 1 ) fclose(OUTFILE0); if ( do_slave1 == 1 ) fclose(OUTFILE1); // Free up the challenges 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]); if ( masks_b != NULL && masks_b[chlng_num] != NULL ) free(masks_b[chlng_num]); } free(challenges_b); if ( masks_b != NULL ) free(masks_b); challenges_b = NULL; masks_b = NULL; gettimeofday(&t1, 0); elapsed = (t1.tv_sec-t0.tv_sec)*1000000 + t1.tv_usec-t0.tv_usec; printf("\tElapsed %ld s\n", (long)elapsed/1000000); fflush(stdout); } } // Tell the device it's done. strcpy(fu_command_string, "EXIT"); if ( do_slave0 == 1 ) if ( SockSendB((unsigned char *)fu_command_string, strlen(fu_command_string) + 1, slave0_client_socket_desc) < 0 ) { printf("ERROR: Send '%s' failed\n", fu_command_string); exit(EXIT_FAILURE); } if ( do_slave1 == 1 ) if ( SockSendB((unsigned char *)fu_command_string, strlen(fu_command_string) + 1, slave1_client_socket_desc) < 0 ) { printf("ERROR: Send '%s' failed\n", fu_command_string); exit(EXIT_FAILURE); } // Return to 25 C and then end labview. if ( labview_present == 1 ) { char response_str[MAX_STRING_LEN]; float present_temperature; sprintf(fu_command_string, "%s %s %d\n", InstrTC, CommandSetTemperature, temperatures[0]); if ( SockSendB((unsigned char *)fu_command_string, strlen(fu_command_string) + 1, labview_client_socket_desc) < 0 ) { printf("ERROR: Send '%s' failed!\n", fu_command_string); exit(EXIT_FAILURE); } if ( SockGetB((unsigned char *)response_str, MAX_STRING_LEN, labview_client_socket_desc) < 0 ) { printf("ERROR: Error receiving final temperature!\n"); exit(EXIT_FAILURE); } sscanf(response_str, "%f", &present_temperature); strcpy(fu_command_string, "DONE\n"); if ( SockSendB((unsigned char *)fu_command_string, strlen(fu_command_string) + 1, labview_client_socket_desc) < 0 ) { printf("ERROR: Send '%s' failed\n", fu_command_string); exit(EXIT_FAILURE); } } if ( do_slave0 == 1 ) { close(slave0_client_socket_desc); close(slave0_server_socket_desc); } if ( do_slave1 == 1 ) { close(slave1_client_socket_desc); close(slave1_server_socket_desc); } if ( labview_present == 1 ) close(labview_server_socket_desc); return 0; }