Skip to content

Chip Execution Queueing

Travis Kehrli edited this page Nov 6, 2021 · 3 revisions

Chip Execution Queueing

Before I dip into this, massive shoutout to the people in the Cylon Discord. Past conversations about this topic and examples provided in discord gave me the threads I needed to start experimenting in-game to sort all this out. Specifically Nyefari's, highCard's, Azurethi's, Firestar99's, and Antani's comments were key.

Ordered Math Requirements

IPS requires certain math done in a specific order.

  1. Get signal strengths
  2. Calculate RawX.
  3. Calculate RawY, depends on RawX.
  4. Calculate RawZ, depends on RawX and RawY.
  5. Calculate RotatedX, RotatedY, RotatedZ, depends on RawX, RawY, RawZ.
  6. Offset coordinates, depends on RotatedX, RotatedY, RotatedZ.

We have a update speed of 0.6 seconds on the single-chip IPS right now, and chances are that will never decrease. So if we want to try and push that update speed lower then we need to separate the steps onto different chips.

Chip Execution Weirdness

From what I understand, chips run on a mean 0.2 second tick. This means that over a given second, the mean execution time for each line is 0.2 seconds. And that means that the actual execution time per line can vary slightly. For instance, line 1 may execute with a time of 0.18 seconds and the second line could execute with a time of 0.22 seconds.

In addition, the order different chips are executed in a given tick is chosen randomly. So not only will the chips not stay in sync time-wise, but they will also not be reliably executed one after another as we need. So we have to force them to behave how we want. To do that, we use ChipWait.

Chipwait

As per the wiki, YOLO chips have a ChipWait field that allows us to control execution of a chip.

  • Negative values = pause execution (note this 'paused time' must last for at least 1 tick)
  • 0 = execute
  • Positive values = delay execution for the set number of ticks

Using that, we can daisy-chain chips so one triggers another in a specific order. For example,

// Chip 1, Chipwait1
:o = "Chip 1 Running" :Chipwait2=0 :Chipwait1=-1 goto1

// Chip 2, Chipwait2
:o = "Chip 2 Running" :Chipwait3=0 :Chipwait2=-1 goto1

// Chip 3, Chipwait3
:o = "Chip 3 Running" :Chipwait1=0 :Chipwait3=-1 goto1

The problem with this is once a chipwait has been set to 0, it will execute on the next tick. As a result, we've split the code across 3 chips but it still takes 3 ticks to execute all the code. To actually make progress, we need to dip into some shenanigans.

Memory Relay

Enter the Memory Relay. Memory Relays take an input memory chip and an output memory chip. Values set in the input memory chip are transferred over to the output memory chip, based on memory field index and regardless of field name. This means if the first field in the input memory chip is named apples and the first field in the output memory chip is named oranges, it doesn't matter. Any changes to apples will be applied to oranges.

Black Magic

Now the black magic part. From what I understand (and I could be wrong), when you set the chipwait of a chip to 0, the execution of that chip is placed into a background game engine execution stack of some kind. If you set another chipwait, then that chip is placed on the top of the stack.

So, we can use the Memory Relay to set chipwait values in order, which then causes the respective chips to execute in order. The trick is to use the Memory Relay to handle the daisy chaining.

Lets walk through a simple example.

First, these are the three chips that have the separate code we want to execute in order.

// Chip 0, Chipwait = Q0
:o = "First Chip"
// Chip 1, Chipwait = Q1
:o += "\nSecond Chip"
// Chip 2, Chipwait = Q2
:o += "\nThird Chip"

Then, we have a chip that handles starting the daisy chaining. Remember that a value of 0 means execute.

// Start Daisychain Chip
:Q3 = 0 goto1

And then we have a chip that resets the daisy chain. Remember that negative values mean pause execution.

// Reset Daisychain Chip, Chipwait = Q3
:Q3 = -1

// And finally we set up our Memory Relay values.

Input Memory Field Output Memory Field
Q3 Q2
Q2 Q1
Q1 Q0

All together, you end up with this flow:

  1. Tick #1 starts.
  2. Q3 is set to -1, which will stop execution next tick.
  3. Chips execute in order Q0 -> Q1 -> Q2 because we are popping off the stack.
  4. Memory Relay sets Q3 => Q2.
  5. Memory Relay sets Q2 => Q1.
  6. Memory Relay sets Q1 => Q0.
  7. Tick #1 ends. Tick #2 starts.
  8. Q3 is set to 0, which will start the daisy chain next tick.
  9. Chips do not execute because their chipwaits are set to -1.
  10. Memory Relay sets Q3 => Q2. Q2 gets pushed onto the execution stack.
  11. Memory Relay sets Q2 => Q1. Q1 gets pushed onto the execution stack.
  12. Memory Relay sets Q1 => Q0. Q0 gets pushed onto the execution stack.
  13. Tick #2 ends. Loop back to start.

And that's how we can execute chips in a deterministic order.

Chaining Daisychains

With one of these, you are limited to executing 10 chips in order. But you can extend the daisychain to include more than 1 Memory Relay by having the second relay pick up where the last one left off. So if the first relay ended on Q10, then the second relay starts on Q10 and continues to Q20.

Limitations

Unfortunately there are a few limitations with this method.

First, it takes 2 ticks to go through the whole execution loop. One tick is used for chip execution and the second tick is use to reset all chipwait values to -1 so it works right the next time around. As far as I am aware, there is no way around this. But I am curious about if there is a way to combine two of these systems to run in opposite states of each other, so while one daisy chain is resetting, the other is executing.

Secondly, having 2 chips simultaneously trying to set the chipwait value of another to -1 and 0 at the same time is normally fine when you have only one pair of them. But when you start to add more of these pairs, chip timings across the ship start to wig out. How much they wig out and whether or not there is a solution still requires some testing.

Clone this wiki locally