S/PDIF receiver internal structure ================================== The S/PDIF receiver is written as a state machine that receives bits and processes those bits, progressively building up a word of received data that is then sent out on a streaming chanend to the next layer in the protocol. The state machine is heavily optimised to run at 192K: a bit rate of 12.288 Mbits/second, requiring a sampling rate of 49.152 MHz. As such, it may look cumbersome at first. The state machine is written in assembly code; the assembly code comprises fragments that are concatenated by a generator. Three sections discuss the general principle of the receiver, the assembly code (SpdifReceive.S) and the generator. We assume that the reader is familiar with the S/PDIF standard. Operating principle ------------------- The receiver has an input port clocked of a clock block that is set to sample the input stream at four times the bit rate. That means, that a zero bit in S/PDIF encoding will appear as four low sampling points or four high sampling points, and a one-bit will appear as two high followed by two low sampling points or vice versa. As the port is sampling data, these will arrive as sampling points 0, 0, 0, 0 (a zero-bit); 1, 1, 1, 1 (also a zero bit); 0, 0, 1, 1 (a one-bit); or 1, 1, 0, 0 (a one bit). These above are ideal sampling sequences; however, as the signal is clocked asynchronously, the sequence 0, 0, 1, 1 may appear as 0, 1, 1, 1 or 0, 0, 0, 1. As the receiver software is not aware of the precise sampling clock, it always runs the sampling clock slightly too fast: 50 MHz for a 192K and 12.5 Mhz for a 48K signal. This means that the sequence 0, 0, 1, 1 may also appear as 0, 0, 1, 1, 1 or 0, 0, 0, 1, 1. Finally, if the signal is received through an optical cable, the duty cycle is hardly ever 50%, also causing a sequence 0, 0, 1, 1 to appear as 0, 0, 0, 1. These values are sampled in a 4-bit buffered port. That is, the port will collect four of those sampling bits, store them and pass them on for processing. That means that on a 192 KHz signal that is sampled at 50 Mhz we have 20 ns x 4 = 80 ns time to dispatch those 4 bits. As the port is buffered, there is some leeway, in that the only strict requirement is every second input from the port is processed in 160 ns. On a 62.5 MIPS thread that leaves us with 5 instructions per input. (!). Each time that a complete bit has been recovered, this bit is shifted into the output-register, and the next bits are processed. The receiver maintains a state of which bits are unprocessed: there are 72 states in the code, and each state is labelled ``Lxy`` where ``x`` is a string of ones and zeros, and ``y`` maybe one of ``_S``, ``_T``, or ``_U``. Ignoring the last bit, the state ``Lx`` means that the receiver has processed all data up to a point in the stream, and is left with a bit string ``x`` that is yet to be processed. This string should be read left to right, with the left being the oldest bit received, and right the most recent bit received. For example, the state 'L0000' means that there is a sequence of four unprocessed low samples. This may indicate a zero-bit in the SPDIF stream, or, it maybe the start of a violation which will nominally comprise six low sample points. It may also be a stream of eight zero bits, which indicates that we are probably oversampling a slower SPDIF stream. The latter is the final part of the system, which is the choice of the sampling clock. Whenever the receiver observes a long string of low or a long string of high sampling points, it will try and half the sampling clock. If it observes a string of alternating high and low samples, then it will try and double the sampling clock as it is probably a faster stream. As 44,100 and 48,000 are only 10% apart in speed, both of those streams can be dealt with by the same sampling clock; 12.5 MHz. 25 Mhz samples both 88,200 and 96,000 and a 50 MHz clock is used for 176,400 and 192,000 sampling rates. The ``_S`` states are used to indicate that a violation has been spotted, ``_T`` states indicate that the first transition after the violation has been processed, and ``_U`` states that the second transition has also been processed. At the time of the ``_T`` state transition the state machine will have sufficient knowledge to know whether the next SPDIF word will be an ``X``, ``Y``, or ``Z`` frame. It therefore outputs the word, and initialises it with a value indicating what sort of frame is coming next. Assembly code ------------- The assembly code uses the following registers: * r0: the input port resource * r1: output chanend resource * r2: initial divider * r3: the clock block resource * r4: temporary value * r5: collected S/PDIF word * r6: overflow from S/PDIF word * r7: the value 1 * r8: the value 0x1A * r9: the value 2 * r10: the value 4 * r11: the value 0 Each state comprises a block of code that follows the following pattern that implements the state machine:: L0111: IN r4, r0 BRU r4 BLRF_u10 L0000_1 BLRB_u10 BERROR BLRB_u10 BERROR BLRB_u10 BERROR BLRB_u10 BERROR BLRB_u10 BERROR BLRB_u10 BERROR BLRB_u10 BERROR BLRF_u10 L0001_1 BLRB_u10 BERROR BLRB_u10 BFASTER BLRB_u10 BERROR BLRF_u10 L0011_1 BLRB_u10 BERROR BLRB_u10 L0111_1 BLRB_u10 BERROR The ``IN r4, r0`` instruction inputs the next four bits from the port into r4, the ``BRU r4`` instruction performs a relative branch based on the bit pattern just received, and the following 16 instructions then jump to the next state. Note that all these instructions are architectural to stop the assembler from allocating long encodings - it must all fit in short encodings, any long encoding is a compile-time error. The ``IN`` instruction inputs 4 bits with the least significant bit being the oldest bit, and the most significant bit (bit 3) being the most recent bit. This is reverse from the convention used for the label names, which is slightly confusing. But at least the label names are written down as we expect it from left to right... So, inspecting the table, the first line says: * Given that we had bits 0111 unprocessed, and we now receive bits 0000, we have received 01110000 which is a one-bit in the SPDIF stream (0111), and then 4 unprocessed bits (0000). Therefor we branch to label ``L0000_1`` meaning state ``L0000`` and we need to record a ``1`` in the SPDIF input word. The second line says: * Given that we had bits 0111 unprocessed, and we now receive bits 1000 (value 0001 reversed), we have received 01111000 which cannot be a valid part of the SPDIF stream. We therefore jump to an ERROR label to resynchronise. And so on. note that there are BERROR and FERROR labels; BERROR jumps backward, and FERROR jumps forward in order to make all labels fit in 10 bits. Similarly, there are FSLOWER and BSLOWER labels, etc. As we have seen, some states need to have labels ``_0`` and ``_1`` to record that a zero-bit or one-bit has been received, and these labels are typically implemented as follows:: L0111_0: LMUL r6, r5, r11, r11, r5, r5 BRFU L0111 .align 4 L0111_1: LMUL r6,r5,r5,r7,r5,r7 L0111: ... The LMUL instruction is a work of marvel that multiplies two numbers (the third and fourth operands) and adds two more numbers (the fifth and sixth operands) into a 64 bit number stored in the first two operands. So, the first LMUL computes r11 x r11 + r5 + r5 = 0 x 0 + r5 + r5 = 2 x r5. This shifts r5 left one bit, shifting any overflow into r6. The second LMUL computes r5 x r7 + r5 + r7 = r5 x 1 + r5 + 1 = 2 x r5 + 1. This shifts r5 left a bit and ors a one bit in the end, shifting any overflow into r6. Other tricks that the receiver occasionally requires shift two bits into r5 simultaneously (multiply by r10) etc. The S, T, and U states work in a very similar manner, except that there are entry points into those states to record whether the next SPDIF word will be an X, Y, or Z frame. An example below is the L000_T state, which is the state where three low samples are unprocessed, we have seen the second transition on the violation, and there are entry points for whether this transition signalled an X, Y, or Z frame:: .align 4 L000_TY: BITREV r5, r5 OUT r1,r5 LADD r5,r6,r8,r11,r11 BRFU L000_T .align 4 L000_TZ: BITREV r5, r5 OUT r1,r5 LADD r5,r6,r8,r9,r11 BRFU L000_T .align 4 L000_TX: BITREV r5, r5 OUT r1,r5 LSUB r5,r6,r8,r7,r11 L000_T: IN r4, r0 BRU r4 ... In any case, the word is bit-reversed and output. The bit reverse is needed as bits are transmitted least significant bit first over SPDIF, but we are shifting them in to the left. After the output, r5 and r6 are initialised using an LADD or LSUB instruction. LADD and LSUB perform an addition (subtraction) with carry (borrow) into two registers: the answer and the carry (borrow). A Y-initialisation adds r8 + r11 + r11 = 0x1A + 0 + 0 = 0x1A into r5 and r6. That is r5 will become 0x1A, and r6 will be 0. A Z-initialisation adds r8 + r9 + r11 = 0x1A + 2 + 0 = 0x1C into r5, and 0 into r6, and finally an X-initialisation computes 0x1A - 1 - 0 = 0x19 into r5 and 0 into r6. Note that the initial value of r5 always has bit 4 set (0x19, 0x1A, 0x1C) and r6 is initially always 0. When 28 bits have been shifted into r5, r5 will be one of 0x9sssssss, 0xAsssssss, or 0xCsssssss and r6 will be 1. This indicates that we have received a full word of data, and is used in some states to jump out of the state machine. On reversing the final value for r5, we will end up with the 28 bits of the SPDIF sample in bits 4..31 and a value of 0x9, 0x5 or 0x3 in the lowest nibble for an X, Y, or Z frame. There are a few cases that are ambiguous; in particular whether a violation has been received or a zero-bit. These are resolved using r6 in labels Lx_CHOICE. The generator ------------- The generator glues together all states. It does so by finding a permutation of the states that enables all jumps to be encoded in 10 bit short operands. This takes a few iterations of a piece of java code. The java code could also find states that overlap, and compile them into a single state. This is not implemented at present. All states are listed in the file ``states.csv`` in this directory. Each state is listed in a row, with the 16 columns listing the 16 next states. These states are listed in order of our left-to-right convention; the actual value input from the port is listed in row 1. This table is generated from the states directory in the generator. You will note that this directory only contains state starting with a '1', as SPDIF is completely symmetrical, the generator will from that create all identical states starting with '0'. Notes ----- #. Normally, on an ERROR a sample of 4 bits is thrown away, and the next 4 bits are used to dispatch to the first state. If compiled with -DTEST it will return on an error, this is useful when debugging the state machine. #. The FASTER function is limited to a clock divider of 1 - it will never go to a 100 MHz clock. #. The SLOWER function is limited to a clock divider of 8. Attempting to go slower than that will set the divider back to 1, this is to avoid aliasing causing a signal to appear slower than it really is. #. The initial value of r5 has bit 5 set, this will cause it to still lock, even though the ERROR might have been thrown around the first bit. #. The state machine can be interrupted by sending a control token over the channel. The control token will be read and the function will return. This goes through an event enable on the input stream, and the event vector being set up for address ``parseSpDifTerminate``.