IOSpec Format ============= The IOSpec contains: * Information about inputs and outputs * Their names (match the FQIR's inputs and output names) * Their sizes (number of elements, before and after padding) * Low-level information like "mailbox id" for inputs * This is no longer required if using sequence-centric IO in SPU library (i.e. :code:`SpuWrite/ReadData()`) * For convenience, information like the quantization scale and the original (unpadded) data length is included as well * Information about "sequences" * Which inputs must be supplied before which each output is produced * Ordering information: the order of the sequences and their inputs and outputs Sequences --------- The IOSpecs describe the "sequences" of input-to-output relationships in the program. For each SPU output, there is a set of inputs which must be provided before the output will be produced. For example, a network that simply performs :code:`A = B + C` will have a single sequence that has output :code:`[A]` and inputs :code:`[B, C]`. The IOSpec for this simple model might look like: .. code-block:: yaml inputs: B: # the input variable's name type: input varname: B # the input variable's name (again) length: 60 # original size in the model padded_length: 64 # this is what is compiled/what FW handles length_64b_words: 16 # equivalent to padded_length, number of 64b words precision: 16 # 16b-element vector quantization: # information about model quantization scale: 1.0 zero_pt: 0.0 core_id: 0 # what core this variable is received on pc: 0 # what pc value is associated with this variable # (mailbox_id is similar, for outputs) comments: # misc information latched: false # hint added by compiler, # whether this input is used in a "latched" sequence C: type: input varname: C # ... other information similar to A's outputs: A: type: output varname: A # ... other information simple_sequences: main_seq: # once B and C have been provided, A will be output type: simple_sequence outputs: - A inputs: - B - C complex_sequences: {} It is illegal to break the strict sequence order given by the IOSpec. For example, providing B twice before providing C will break the SPU's internal control. Similarly, providing, B, then C, then B again before receiving A will also cause problems. In FW, the SPU library will emit errors (e.g. :code:`kSpuSequenceError`) if the order is not respected. Similarly, the IOSpec's rules will be checked when running the hardware or simulation using Femtodriver. The order of the inputs and outputs *within the sequence* is important when using the SPU library. In the above, B comes before C in the main_seq. The SPU library's :code:`SpuWriteData()` requires the vectors to be written in the order in the associated sequence. Similarly, the order that data is returned from :code:`SpuReadData()` will match the order in the sequence (e.g. if there were two outputs, X followed by Y, X will come out first). Complex sequences are sequences with non-1:1 relationships between inputs and outputs, e.g. a complex sequence would be used if a particular output was only generated every other time an input was sent. This is not currently used by any model and is not yet supported by SPU library FW. Multiple Sequences ------------------ With the exception of latched sequences (see below), multiple sequences are not yet supported. The :code:`sequence_id` provided to :code:`SpuWrite/ReadData()` in SPU library is the order that the sequence occurs in the IOSpec file. For example, in the latched inputs example below, :code:`main_seq` occurs first and would be sequence id 0, and :code:`latched_seq` is sequence id 1. Latched Inputs -------------- Latched inputs are simply inputs that are part of a sequence with no associated output. Suppose we wanted to do the same math as before: :code:`A = B + latchedC`, but we only wanted to update latchedC infrequently, producing A whenever B is provided (maybe latchedC is like a model tuning parameter, and we do not want to send a new value every frame). When constructing the FQIR, the user will have specified that latchedC is a latched variable. The following IOSpec might be produced: .. code-block:: yaml inputs: B: type: input varname: B # ... other information latchedC: type: input varname: C # ... other information comments: latched: true # useful hint, but redundant with the sequence info outputs: A: type: output varname: A # ... other information simple_sequences: main_seq: # sequence id 0 in SPU library FW # when B (and only B) is provided, A will be output type: simple_sequence outputs: - A inputs: - B # no entry for latchedC! latched_seq: # sequence id 1 in SPU library FW type: simple_sequence # the latched input sequence for latchedC (no output) outputs: [] inputs: - latchedC complex_sequences: {} With the above, it is perfectly valid to provide B only, without providing C, each time we drive inputs to the model. For example: * write latched_C = 1, write B = 1 * read A = 2 * write B = 2 * read A = 3 * write B = 3 * read A = 4 * write latched_C = 2, write B = 4 * read A = 6 Is a valid ordering of transactions. Latched inputs will be initialized to 0, override their values before starting the model, if necessary.