Watchdog

The FPGA has a watchdog which, when fed properly, enables all the outputs on the robot (e.g. PWM, Relays, CAN (albeit, indirectly), etc).

Danger

Manually feeding the watchdog bypasses all the built in safeties preventing the robot from moving. Do not manually feed the watchdog on a robot that has any chance of injuring someone.

Feeding

The 2019 FPGA watchdog was updated to be slightly obfuscated, but is still based on a single byte challenge-response, so still has the same problem.

  1. Write 0xb007 to SysWatchdog.Command
  2. Read from SysWatchdog.Challenge (4 bytes as of 2019)
  3. Apply the following deobfuscation to the challenge:
uint32_t swapped = obfuscated;

swapped = ((swapped >> 1) & (0x55555555)) | ((swapped & (0x55555555)) << 1);
// Swap consecutive pairs
swapped = ((swapped >> 2) & (0x33333333)) | ((swapped & (0x33333333)) << 2);
// Swap nibbles
swapped = ((swapped >> 4) & (0x0f0f0f0f)) | ((swapped & (0x0f0f0f0f)) << 4);

// XOR outer bytes of swapped with inner bytes of original obfuscated challenge
challenge = ( (swapped >> 24) ^ (obfuscated >> 16) ^ (obfuscated >> 8) ^ swapped ) & 0xff;
  1. The official code combines the response with random data to reobfuscate it. The result is that the lower two bytes XORed together produce the response.
response = 0xcc | ((0xcc ^ responses[challenge]) << 8)

Note

This can be any byte (0xcc used above) other than 0x00. Using 0x00 will cause the watchdog to die when the response is 0x00. This is what lead to FIRST to release a late build season image, since the original didn’t check.

When enabled, every 20ms there’s a \(\frac{1}{256}\) chance for the (lower byte of the) random obfuscation number to be 0, and a \(\frac{1}{256}\) chance for the response to be 0. When both were 0 (on average, every 21.8 minutes), the watchdog would die.

  1. Write the response to SysWatchdog.Command
  2. Write 0xfeed to SysWatchdog.Command and go to step 2

To stop feeding, one should write 0xdead to SysWatchdog.Command

Challenge-response

The challenge-response is a single byte in 2019, just obfuscated, so simply intercepting, deobfuscating, and recording all the proper responses is trivial.

The current responses are:

uint8_t responses[256] = {
 118, 154, 243,  85,  81,  32, 153, 216, 137, 123,  26,  45, 165,  28, 100,   2,
 168, 234,  61, 231, 155, 250, 248,  74,  58, 230, 246,  69,  77, 163,   7,  52,
 106, 239, 240,  64, 188, 147, 254,   5, 126, 138, 121, 113, 229, 179,  49, 161,
 109,  57,  27,  18,  86, 148, 195,  55, 222, 211, 117, 210, 104,   3, 226,  67,
 209, 217, 177,  12, 219, 101,  73, 145,  99, 164, 215, 214,  94,  78,  17,   0,
 169, 225, 245, 119,  53, 157, 224, 107, 212,  51, 218,  63,   8, 174, 252,  96,
 184, 223, 105,  38, 129,  82,  92, 134, 194,  80, 102,  48, 146,  36, 204, 242,
 238, 253, 130,  40, 152, 182,  70, 178, 247,  84, 187, 235, 208,  42, 202,  71,
  23,  54, 141, 175, 180,  20,  89,  29,  97, 108, 221, 156, 206, 236, 189, 143,
   9,  30, 125,  35,  87, 144, 159,  90, 114, 191, 139,   6, 201, 171, 228, 213,
  15,  65, 232,  50,  79, 150, 220, 133, 116, 181, 185,  16, 190,  91, 200,  47,
 197, 203,  68,  21,  43, 167, 199, 241, 142, 255, 183, 158, 122, 251,  59, 176,
  39,   1, 192,  83, 124,  37, 115, 132, 128,  22, 244, 249,  93,  46, 227,  11,
 149, 172, 196, 136, 193, 207,  76,  13, 162,  19, 170,  10,  66, 131,  44, 110,
  75,  31, 103,  33,  56, 198, 140, 120, 166,  72,  98,  95, 127, 112, 160,  25,
  34, 186, 173, 135,  88,  14,   4, 237,  62,  24, 233,  60, 111, 205, 151,  41
};