XJRunner can generate / read and log unique board identifiers during testing, such as serial numbers and MAC addresses. This application note describes how these should be handled by the XJEase code.
Serial numbers (or other unique board identifiers) are passed between the XJEase code and XJRunner using the built-in global string variable SERIAL_NUMBER
. There are two aspects of handling serial numbers:
Generating Serial Numbers
In XJRunner, the Serial Number tab of the Project Manager provides three ways of generating serial numbers.
- XJRunner generates serial numbers
- XJRunner can generate simple incrementing serial numbers with a prefix and/or suffix, such as the range SN0001B – SN9999B. If more complex board identifiers are needed, such as MAC addresses, there are two other options:
- User inputs serial numbers
- For example, the boards may have a barcode with their serial number. Using a barcode reader, the tester can input these into XJRunner.
- Take serial numbers from the test system
- Just as XJRunner can generate serial numbers and pass them to XJEase for programming into the board, XJEase can generate serial numbers and pass them back to XJRunner for logging. We will now look at this option in more detail.
Generating serial numbers in XJEase
If XJEase will be generating the serial numbers, it must do so in a function with the name NEW_SERIAL_NUMBER
. Using this name lets XJRunner know when a new serial number has been generated, so that it can log the value of the variable SERIAL_NUMBER
.
The function NEW_SERIAL_NUMBER
is just an XJEase test function from the XJEase project file – it must also be listed in the TEST LIST
section of the project preamble.
It is generally a good idea to put serial number functions at the end of your TEST LIST
, so that you can be sure the board has passed the tests before assigning it a serial number.
Example code:
[Circuit Preamble]
TEST LIST
...
"Program Serial Number"
NEW_SERIAL_NUMBER; // Set up the global variable SERIAL_NUMBER.
IC3.ProgSerNo; // Program the new serial number into EEPROM.
// See below for more details.
END;
...
END
...
[after preamble]
// An example of how to increment a serial number held in a file.
NEW_SERIAL_NUMBER()(INT Failed)
INT serial WIDTH 32;
FILE file;
INT result;
Failed := FALSE;
// Open the file.
FOPEN("CurrentSerialNumber.txt", "r+")(file);
IF FERROR() THEN
PRINT("Error opening CurrentSerialNumber.txt\n");
Failed := TRUE;
RETURN;
END;
// Wait for the file lock to become available.
DO
FLOCK(file, 1)(result);
WHILE result != 0
END;
// Read the previous serial number from the file.
// N.B. Serial numbers are 32 bits.
FGETI(file, 32)(serial);
// Increment the serial number.
serial := serial + 1;
SERIAL_NUMBER := FORMAT(serial, "%i");
// Return to the beginning of the file.
FSEEK(file, 0, 0);
// Save the new serial number.
FWRITE(file, serial);
IF FERROR() THEN
PRINT("Error writing to CurrentSerialNumber.txt\n");
Failed := TRUE;
RETURN;
END;
// Unlock the file and close it.
FUNLOCK(file);
FCLOSE(file);
END;
Programming Serial Numbers
Once the variable SERIAL_NUMBER
has been set up (either by XJRunner or by a NEW_SERIAL_NUMBER
function), the serial number will then typically be programmed into the board.
Example code:
[Circuit Preamble]
TEST LIST
...
"Program Serial Number"
NEW_SERIAL_NUMBER; // Set the global variable SERIAL_NUMBER.
IC3.ProgSerNo; // Program the new serial number into EEPROM.
END;
...
END
[Test Code for IC3 (EEPROM) in device file]
ProgSerNo()(INT Failed)
INT serNo1, serNo2, serNo3, serNo4;
INT serial WIDTH 32;
// Convert serial number string to an integer.
serial := STRTOINT(SERIAL_NUMBER);
// Write four data bytes to the EEPROM.
I2C_Write(0x0, I2C_ADDRESS, serial[7..0]);
I2C_Write(0x1, I2C_ADDRESS, serial[15..8]);
I2C_Write(0x2, I2C_ADDRESS, serial[23..16]);
I2C_Write(0x3, I2C_ADDRESS, serial[31..24]);
// Read the bytes back.
I2C_Read(0, I2C_ADDRESS)(serNo1);
I2C_Read(1, I2C_ADDRESS)(serNo2);
I2C_Read(2, I2C_ADDRESS)(serNo3);
I2C_Read(3, I2C_ADDRESS)(serNo4);
// Check the read-back data.
IF( (serNo1 != serial[7..0]) ||
(serNo2 != serial[15..8]) ||
(serNo3 != serial[23..16]) ||
(serNo4 != serial[31..24]) ) THEN
Failed := 1; // Fail
ELSE
Failed := 0; // Pass
END;
END;