This commit is contained in:
Steven Dan
2025-12-11 09:43:42 +08:00
commit d8b2974133
1822 changed files with 280037 additions and 0 deletions

View File

@@ -0,0 +1,307 @@
// Copyright 2011-2021 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
import java.io.*;
import java.util.*;
class Arrange {
String labels[] = {
// "L11d",
// "L1d",
// "L111d",
"L1000",
"L100",
"L10",
"L11100",
"L11000",
"L1110",
"L1100",
"L110",
"L111",
"L1111",
"L11111",
"L11111a",
"L1111111",
"L111111",
"L1",
"L11",
"L1_S",
"L11100_S",
"L11_S",
"L111_S",
"L1111_S",
"L11111_S",
"L111111_S",
"L1111111_S",
"L1_U",
"L11_U",
"L111_U",
"L1111_U",
"L11111_U",
"L111111_U",
"L1111111_U",
"L1_T",
"L11_T",
"L111_T",
// "L1110_S",
"L1111100_S",
"L111110_S",
};
static HashMap<String,Block> entryPoints = new HashMap<String,Block>();
static Vector<Block> blockVector = new Vector<Block>();
static int blockCount = 0;
static int cutOff = 50;
static Block error =null;
void run() {
entryPoints.put("ERROR", new Block("ERROR", -1));
entryPoints.put("FASTER", new Block("FASTER", -2));
entryPoints.put("SLOWER", error = new Block("SLOWER", -3));
for (String s: labels) {
Block b = new Block(s, blockCount);
blockVector.add(b);
blockCount++;
b = new Block(b, blockCount);
blockVector.add(b);
blockCount++;
}
for(Block b: blockVector) {
b.patchReferences();
}
for(Block b: blockVector) {
if (!b.used) {
System.err.println("No reference to " + b.bbName);
}
}
for(int i = 0; i < 15; i++) { // 15
optimise(20); // 20
System.err.println("Cost " + cost() + " (cutoff " + cutOff + ")");
if (cost() < 100000) {
cutOff--;
}
}
copy("headerS");
for(Block b: blockVector) {
System.out.println(b.theCode());
}
copy("trailerS");
}
int cost() {
int costs = 0;
for(int loc = 0; loc < blockCount; loc++) {
Block b = blockVector.get(loc);
costs += b.cost();
}
return costs;
}
void swapBlocks(int j, int k) {
Block a = blockVector.get(j);
Block b = blockVector.get(k);
blockVector.set(j, b);
blockVector.set(k, a);
a.location = k;
b.location = j;
}
void optimise(int n) {
int minCost = cost();
int minJ = -1;
int minK = -1;
for(int i = 0; i < n; i++) {
System.err.println(" -- Cost " + minCost);
for(int j = 0; j < blockCount; j++) {
for(int k = j+1; k < blockCount; k++) {
swapBlocks(j, k);
if (cost() < minCost) {
minJ = j;
minK = k;
minCost = cost();
}
swapBlocks(j, k);
}
}
if (minJ != -1) {
swapBlocks(minJ, minK);
} else {
break;
}
if (minCost < 100000) {
break;
}
}
}
void copy(String s) {
try {
BufferedReader bi = new BufferedReader(new FileReader("states/" + s));
while(true) {
String line = bi.readLine();
if (line == null) {
return;
}
System.out.println(line);
}
} catch (Exception e) {
System.err.println("Exception " + e + " on " + s);
}
}
public static void main(String args[]) {
new Arrange().run();
}
}
class Block {
Vector<Block> refs = new Vector<Block>();
Vector<String> refsI = new Vector<String>();
String bbName;
int location;
boolean used = false;
String code = "";
Block(String s, int location) {
boolean beforeBru= true;
bbName = s;
this.location = location;
if (!s.startsWith("L")) {
return;
}
try {
BufferedReader bi = new BufferedReader(new FileReader("states/" + s));
while(true) {
String line = bi.readLine();
if (line == null) {
return;
}
if (beforeBru) {
code = code + line + "\n";
}
if (line.contains("BRU")) {
beforeBru= false;
}
if (line.contains(":")) {
addEntrypoint(line,this);
}
if (!beforeBru) {
if (line.contains("SLOWER")) {
addReference("SLOWER");
} else if (line.contains("FASTER")) {
addReference("FASTER");
} else if (line.contains("ERROR")) {
addReference("ERROR");
} else if ((line.contains(" BL") || line.contains(" BR")) && line.contains(" L")) {
addReference(line);
}
}
}
} catch (Exception e) {
System.err.println("Exception " + e + " in " + s);
}
}
Block(Block b, int location) {
bbName = inverse(b.bbName);
String lines[] = b.code.split("\n");
for(String l: lines) {
if (l.contains(":")) {
String i = inverse(l);
code += i + "\n";
addEntrypoint(i,this);
} else if (l.contains(" bf ") || l.contains(" bt ") || l.contains(" bu ") ) {
code += inverse(l) + "\n";
} else if (l.contains(" BR")) {
code += inverse(l) + "\n";
} else {
code += l + "\n";
}
}
this.location = location;
for(String s: b.refsI) {
refsI.add(0, inverse(s));
}
}
String inverse(String s) {
boolean inversing = true;
String r = "";
for(int i = 0; i < s.length(); i++) {
if (inversing && s.charAt(i) == '0') {
r += '1';
} else if (inversing && s.charAt(i) == '1') {
r += '0';
} else if (s.charAt(i) == '_') {
r += '_';
inversing = false;
} else {
r += s.charAt(i);
}
}
return r;
}
void addEntrypoint(String line, Block b) {
String label = line.replaceAll(".* L","L").replaceAll(":.*","");
Arrange.entryPoints.put(label, b);
}
void addReference(String line) {
String label1 = line.replaceFirst(".* L","L").replaceAll("[ /].*","");
String label = label1.replaceAll("[ /].*","").replaceAll("z","");
// System.out.println("Line " + line + " label " + label1 + " -- " + label);
refsI.add(label);
}
void patchReferences() {
for(String s: refsI) {
Block b = Arrange.entryPoints.get(s);
if (b == null) {
System.err.println("Label " + s + " not found in " + bbName);
refs.add(Arrange.error);
} else {
// System.err.println("Label " + s + " found in " + bbName);
b.used = true;
refs.add(b);
}
}
}
int cost() {
int costs = 0;
for(Block b: refs) {
if (b.location < 0) {
continue;
}
int c = location - b.location;
if (c < 0) {
c = -c;
}
if (c > Arrange.cutOff) {
c += 100000;
}
costs += c;
}
return costs;
}
String theCode() {
int k =0;
for(Block b : refs) {
if (b.location < 0) {
if (location < Arrange.blockCount / 2) {
code += " BLRB_u10 B" + b.bbName + "\n";
} else {
code += " BLRF_u10 F" + b.bbName + "\n";
}
} else if (b.location <= location) {
code += " BLRB_u10 " + refsI.get(k) + "\n";
} else {
code += " BLRF_u10 " + refsI.get(k) + "\n";
}
k++;
}
return code;
}
}

View File

@@ -0,0 +1,88 @@
#!/bin/sh
echo ',0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,15'
echo ',"0000","0001","0010","0011","0100","0101","0110","0111","1000","1001","1010","1011","1100","1101","1110","1111"'
cd states
for pat in "L*[01a]" "L*S" "L*T" "L*U"
do
> /tmp/xxx
for i in $pat
do
awk < $i '
/BRU/{
lookup[0] = 0;
lookup[1] = 8;
lookup[2] = 4;
lookup[3] = 12;
lookup[4] = 2;
lookup[5] = 10;
lookup[6] = 6;
lookup[7] = 14;
lookup[8] = 1;
lookup[9] = 9;
lookup[10] = 5;
lookup[11] = 13;
lookup[12] = 3;
lookup[13] = 11;
lookup[14] = 7;
lookup[15] = 15;
for(i = 0; i < 16; i++) {
lookupi[i] = 15 - lookup[i];
}
capture = 1;
pattern = 0;
next;
}
(capture == 1) {
if ($2 == "ERROR") {
patterns[pattern++] = "-";
} else {
patterns[pattern++] = $2;
}
if (pattern == 16) {
printf("'$i',");
for(i = 0; i < 16; i++) {
printf ("\"%s\",", patterns[lookup[i]]);
}
printf("\n");
z="'$i'";
zi = "";
for(j = 1; j <= length(z); j++) {
if (substr(z,j,1) == "0") {
zi = zi "1";
} else if (substr(z,j,1) == "1") {
zi = zi "0";
} else {
zi = zi substr(z, j, 1);
}
}
printf("%s,", zi);
for(i = 0; i < 16; i++) {
zi = "";
z = patterns[lookupi[i]];
conv = 1;
for(j = 1; j <= length(z); j++) {
if (conv && substr(z,j,1) == "0") {
zi = zi "1";
} else if (conv && substr(z,j,1) == "1") {
zi = zi "0";
} else {
if (substr(z, j, 1) == "_") {
conv = 0;
}
zi = zi substr(z, j, 1);
}
}
printf ("\"%s\",", zi);
}
printf("\n");
}
}
' > /tmp/xx
head -1 /tmp/xx
tail -1 /tmp/xx >> /tmp/xxx
done
cat /tmp/xxx
rm -f /tmp/xxx /tmp/xx
done

View File

@@ -0,0 +1,3 @@
#!/bin/sh
javac Arrange.java
java Arrange > ../../lib_spdif/src/SpdifReceive.S

View File

@@ -0,0 +1,274 @@
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``.

View File

@@ -0,0 +1,44 @@
.align 4
L1_Y:
BITREV r5, r5
OUT r1,r5
LADD r5,r6,r8,r11,r11
BRFU L1
.align 4
L1_X:
BITREV r5, r5
OUT r1,r5
LSUB r5,r6,r8,r7,r11
BRFU L1
.align 4
L1_01:
LMUL r6,r5,r5,r10,r7,r11
BRFU L1
.align 4
L1_1:
LMUL r6,r5,r5,r7,r5,r7
BRFU L1
.align 4
L1_00:
LMUL r6, r5, r11, r11, r5, r5
L1_0:
LMUL r6, r5, r11, r11, r5, r5
L1:
IN r4, r0
BRU r4
BLRF_u10 ERROR
BLRB_u10 L11000
BLRF_u10 ERROR
BLRB_u10 L11100
BLRF_u10 ERROR
BLRF_u10 ERROR
BLRF_u10 ERROR
BLRB_u10 L0_0
BLRB_u10 L1_1
BLRB_u10 L1_1
BLRB_u10 FASTER
BLRB_u10 L1_1
BLRF_u10 L11_1
BLRF_u10 L11_1
BLRF_u10 ERROR
BLRB_u10 L11111

View File

@@ -0,0 +1,37 @@
.align 4
L10_Y:
BITREV r5, r5
OUT r1,r5
LADD r5,r6,r8,r11,r11
BRFU L10
.align 4
L10_CHOICE:
.syntax default
bt r6, L0_TZ
.syntax architectural
L10_0:
LMUL r6, r5, r11, r11, r5, r5
BRFU L10
.align 4
L10_1:
LMUL r6,r5,r5,r7,r5,r7
L10:
IN r4, r0
BRU r4
BLRF_u10 ERROR
BLRF_u10 ERROR
BLRF_u10 ERROR
BLRF_u10 ERROR
BLRB_u10 L10_1
BLRF_u10 FASTER
BLRF_u10 L110_1
BLRF_u10 ERROR
BLRF_u10 ERROR
BLRF_u10 ERROR
BLRF_u10 ERROR
BLRF_u10 ERROR
BLRF_u10 L11_1
BLRF_u10 ERROR
BLRF_u10 L111_1
BLRF_u10 ERROR

View File

@@ -0,0 +1,30 @@
.align 4
L100_CHOICE:
.syntax default
bt r6, L00_TZ
.syntax architectural
L100_0:
LMUL r6, r5, r11, r11, r5, r5
BRFU L100
.align 4
L100_1:
LMUL r6,r5,r5,r7,r5,r7
L100:
IN r4, r0
BRU r4
BLRF_u10 ERROR
BLRF_u10 ERROR
BLRB_u10 L100_1
BLRF_u10 L1100_1
BLRF_u10 ERROR
BLRF_u10 FASTER
BLRF_u10 L110_1
BLRF_u10 L1110_1
BLRF_u10 ERROR
BLRF_u10 ERROR
BLRF_u10 ERROR
BLRF_u10 ERROR
BLRF_u10 ERROR
BLRF_u10 ERROR
BLRF_u10 L111_1
BLRF_u10 L1111_1

View File

@@ -0,0 +1,30 @@
.align 4
.syntax default
L1000_CHOICE:
bt r6, L000_TZ
.syntax architectural
L1000_0:
LMUL r6, r5, r11, r11, r5, r5
BRFU L1000
.align 4
L1000_1:
LMUL r6,r5,r5,r7,r5,r7
L1000:
IN r4, r0
BRU r4
BLRF_u10 ERROR
BLRB_u10 L1000_1
BLRF_u10 ERROR
BLRF_u10 L1100_1
BLRF_u10 ERROR
BLRF_u10 FASTER
BLRF_u10 ERROR
BLRF_u10 L1110_1
BLRF_u10 ERROR
BLRF_u10 ERROR
BLRF_u10 ERROR
BLRF_u10 ERROR
BLRF_u10 ERROR
BLRF_u10 ERROR
BLRF_u10 ERROR
BLRF_u10 L1111_1

View File

@@ -0,0 +1,42 @@
.align 4
L11_Y:
BITREV r5, r5
OUT r1,r5
LADD r5,r6,r8,r11,r11
BRFU L11
.align 4
L11_X:
BITREV r5, r5
OUT r1,r5
LSUB r5,r6,r8,r7,r11
BRFU L11
.align 4
L11_0:
LMUL r6,r5,r11,r11,r5,r5
BRFU L11
.align 4
L11_01:
LMUL r6,r5,r5,r10,r7,r11
BRFU L11
.align 4
L11_1:
LMUL r6,r5,r5,r7,r5,r7
L11:
IN r4, r0
BRU r4
BLRF_u10 ERROR
BLRF_u10 L000_0
BLRF_u10 ERROR
BLRF_u10 L00_0
BLRF_u10 L10_1
BLRB_u10 FASTER
BLRF_u10 ERROR
BLRB_u10 L0_S
BLRB_u10 L1_1
BLRB_u10 L1_1
BLRB_u10 FASTER
BLRB_u10 L01_0
BLRB_u10 L11_1
BLRB_u10 L11_1
BLRB_u10 L111_1
BLRB_u10 L111111

View File

@@ -0,0 +1,26 @@
.align 4
L110_0:
LMUL r6, r5, r11, r11, r5, r5
BRFU L110
.align 4
L110_1:
LMUL r6,r5,r5,r7,r5,r7
L110:
IN r4, r0
BRU r4
BLRF_u10 ERROR
BLRF_u10 ERROR
BLRF_u10 L100_1
BLRF_u10 ERROR
BLRB_u10 L10_1
BLRB_u10 FASTER
BLRB_u10 L110_1
BLRB_u10 L1110_1
BLRF_u10 ERROR
BLRF_u10 ERROR
BLRF_u10 ERROR
BLRF_u10 ERROR
BLRF_u10 L11_1
BLRF_u10 ERROR
BLRF_u10 L111_1
BLRF_u10 L1111_1

View File

@@ -0,0 +1,27 @@
.align 4
L1100_1:
LMUL r6,r5,r5,r7,r5,r7
BRFU L1100
.align 4
L1100_0:
LMUL r6, r5, r11, r11, r5, r5
L1100:
IN r4, r0
BRU r4
BLRF_u10 ERROR
BLRF_u10 L1000_1
BLRB_u10 L100_1
BLRB_u10 L1100_1
BLRF_u10 ERROR
BLRB_u10 FASTER
BLRF_u10 L110_1
BLRB_u10 L1110_1
BLRF_u10 ERROR
BLRF_u10 ERROR
BLRF_u10 ERROR
BLRF_u10 ERROR
BLRF_u10 ERROR
BLRF_u10 ERROR
BLRF_u10 L111_1
BLRF_u10 L1111_1

View File

@@ -0,0 +1,20 @@
L11000:
IN r4, r0
BRU r4
BLRF_u10 ERROR
BLRB_u10 L1000_1
BLRF_u10 ERROR
BLRF_u10 L1100_1
BLRF_u10 ERROR
BLRB_u10 FASTER
BLRF_u10 ERROR
BLRF_u10 L1110_1
BLRF_u10 ERROR
BLRF_u10 ERROR
BLRF_u10 ERROR
BLRF_u10 ERROR
BLRF_u10 ERROR
BLRF_u10 ERROR
BLRF_u10 ERROR
BLRF_u10 L1111_1

View File

@@ -0,0 +1,32 @@
.align 4
L111_Y:
BITREV r5, r5
OUT r1,r5
LADD r5,r6,r8,r11,r11
BRFU L111
.align 4
L111_0:
LMUL r6, r5, r11, r11, r5, r5
BRFU L111
.align 4
L111_1:
LMUL r6,r5,r5,r7,r5,r7
L111:
IN r4, r0
BRU r4
BLRB_u10 L0000_0
BLRF_u10 L000_0
BLRB_u10 FASTER
BLRB_u10 L00_S
BLRF_u10 L10_1
BLRF_u10 ERROR
BLRB_u10 L110_1
BLRF_u10 L0_S
BLRB_u10 L0001_0
BLRF_u10 L001_0
BLRB_u10 FASTER
BLRF_u10 L01_0
BLRF_u10 L11_1
BLRB_u10 L011_0
BLRB_u10 L111_1
BLRF_u10 L1111111

View File

@@ -0,0 +1,30 @@
.align 4
L1110_CHOICE:
.syntax default
bt r6, L0_TZ
.syntax architectural
L1110_0:
LMUL r6, r5, r11, r11, r5, r5
BRFU L1110
.align 4
L1110_1:
LMUL r6,r5,r5,r7,r5,r7
L1110:
IN r4, r0
BRU r4
BLRF_u10 L00000_0
BLRF_u10 ERROR
BLRB_u10 L100_1
BLRF_u10 L1100_1
BLRF_u10 L0_01
BLRB_u10 FASTER
BLRF_u10 L110_1
BLRB_u10 L1110_1
BLRF_u10 L1_00
BLRF_u10 ERROR
BLRF_u10 ERROR
BLRF_u10 ERROR
BLRB_u10 L00011_0
BLRF_u10 ERROR
BLRF_u10 L111_1
BLRF_u10 L1111_1

View File

@@ -0,0 +1,23 @@
.align 4
L11100_0:
LMUL r6, r5, r11, r11, r5, r5
L11100:
IN r4, r0
BRU r4
BLRF_u10 L000000_0
BLRF_u10 L1000_1
BLRF_u10 L00_01
BLRF_u10 L1100_1
BLRF_u10 ERROR
BLRB_u10 FASTER
BLRF_u10 L0_01
BLRF_u10 L1110_1
BLRF_u10 L1_00
BLRF_u10 ERROR
BLRF_u10 ERROR
BLRF_u10 ERROR
BLRF_u10 ERROR
BLRF_u10 ERROR
BLRF_u10 ERROR
BLRF_u10 L1111_1

View File

@@ -0,0 +1,21 @@
L11100_S:
IN r4, r0
BRU r4
BLRB_u10 ERROR
BLRB_u10 ERROR
BLRB_u10 ERROR
BLRB_u10 ERROR
BLRB_u10 ERROR
BLRB_u10 ERROR
BLRB_u10 ERROR
BLRB_u10 ERROR
BLRB_u10 ERROR
BLRB_u10 ERROR
BLRB_u10 ERROR
BLRB_u10 ERROR
BLRB_u10 ERROR
BLRB_u10 ERROR
BLRF_u10 L111_UY
BLRF_u10 L1111_UZ

View File

@@ -0,0 +1,27 @@
.align 4
L1111_0:
LMUL r6, r5, r11, r11, r5, r5
BRFU L1111
.align 4
L1111_1:
LMUL r6,r5,r5,r7,r5,r7
L1111:
IN r4, r0
BRU r4
BLRB_u10 L0000_0
BLRB_u10 L000_S
BLRF_u10 ERROR
BLRF_u10 L00_S
BLRF_u10 L0_01
BLRF_u10 ERROR
BLRF_u10 L0_01
BLRF_u10 L0_S
BLRB_u10 L0001_0
BLRF_u10 L001_0
BLRB_u10 FASTER
BLRF_u10 L01_CHOICE
BLRB_u10 L0011_0
BLRB_u10 L011_0
BLRF_u10 L0111_0
BLRF_u10 L1111111

View File

@@ -0,0 +1,25 @@
.align 4
L11111_0:
LMUL r6, r5, r11, r11, r5, r5
L11111:
.syntax default
bt r6, L11111a
.syntax architectural
IN r4, r0
BRU r4
BLRB_u10 L0000_0
BLRF_u10 L000_0
BLRF_u10 ERROR
BLRF_u10 ERROR
BLRF_u10 L0_01
BLRF_u10 ERROR
BLRF_u10 L0_01
BLRF_u10 ERROR
BLRB_u10 L0001_0
BLRF_u10 L001_0
BLRB_u10 FASTER
BLRF_u10 ERROR
BLRB_u10 L0011_0
BLRF_u10 L011_0
BLRB_u10 L0111_0
BLRF_u10 SLOWER

View File

@@ -0,0 +1,20 @@
L1111100_S:
IN r4, r0
BRU r4
BLRB_u10 ERROR
BLRF_u10 L000_Y
BLRF_u10 L00_X
BLRF_u10 L00_Y
BLRB_u10 ERROR
BLRB_u10 ERROR
BLRB_u10 L0_X
BLRF_u10 L0_Y
BLRB_u10 ERROR
BLRB_u10 ERROR
BLRB_u10 ERROR
BLRF_u10 L01_Y
BLRB_u10 ERROR
BLRB_u10 ERROR
BLRB_u10 L111_UX
BLRF_u10 L1111_UY

View File

@@ -0,0 +1,22 @@
L111110_S:
IN r4, r0
BRU r4
BLRB_u10 ERROR
BLRF_u10 L000_Y
BLRF_u10 L00_Y
BLRF_u10 L00_Y
BLRB_u10 L0_X
BLRB_u10 ERROR
BLRF_u10 L0_Y
BLRF_u10 L0_Y
BLRB_u10 ERROR
BLRB_u10 ERROR
BLRB_u10 ERROR
BLRB_u10 ERROR
BLRB_u10 L11_UX
BLRB_u10 ERROR
BLRB_u10 L111_UY
BLRF_u10 L1111_UY

View File

@@ -0,0 +1,23 @@
.align 4
L111111_0:
LMUL r6, r5, r11, r11, r5, r5
L111111:
IN r4, r0
BRU r4
BLRF_u10 L0000_S
BLRF_u10 L000_S
BLRF_u10 ERROR
BLRF_u10 L00_S
BLRF_u10 L0_UZ
BLRF_u10 ERROR
BLRF_u10 L0_UZ
BLRF_u10 SLOWER
BLRF_u10 L0001_CHOICE
BLRF_u10 L1_TZ
BLRB_u10 FASTER
BLRF_u10 L1_TZ
BLRF_u10 L11_TZ
BLRF_u10 L11_TZ
BLRF_u10 L0111_CHOICE
BLRB_u10 SLOWER

View File

@@ -0,0 +1,20 @@
L1111111:
IN r4, r0
BRU r4
BLRF_u10 L0000_S
BLRF_u10 L000_S
BLRF_u10 ERROR
BLRF_u10 ERROR
BLRF_u10 L0_UZ
BLRF_u10 ERROR
BLRF_u10 L0_UZ
BLRB_u10 SLOWER
BLRF_u10 L1_TY
BLRF_u10 ERROR
BLRB_u10 FASTER
BLRF_u10 ERROR
BLRF_u10 L11_TZ
BLRF_u10 L11_TZ
BLRF_u10 L111_TZ
BLRB_u10 SLOWER

View File

@@ -0,0 +1,20 @@
L1111111_S:
IN r4, r0
BRU r4
BLRB_u10 ERROR
BLRB_u10 ERROR
BLRB_u10 ERROR
BLRB_u10 ERROR
BLRF_u10 L0_X
BLRB_u10 ERROR
BLRF_u10 L0_X
BLRB_u10 ERROR
BLRF_u10 L1_UX
BLRB_u10 ERROR
BLRB_u10 ERROR
BLRB_u10 ERROR
BLRF_u10 L11_UX
BLRB_u10 L11_UX
BLRF_u10 L111_UX
BLRB_u10 ERROR

View File

@@ -0,0 +1,20 @@
L1111111_U:
IN r4, r0
BRU r4
BLRF_u10 L0000
BLRB_u10 L000
BLRB_u10 ERROR
BLRB_u10 ERROR
BLRB_u10 ERROR
BLRB_u10 ERROR
BLRB_u10 L0_1
BLRB_u10 ERROR
BLRB_u10 L0001
BLRB_u10 ERROR
BLRB_u10 ERROR
BLRB_u10 ERROR
BLRF_u10 L0011
BLRB_u10 ERROR
BLRB_u10 L0111
BLRB_u10 ERROR

View File

@@ -0,0 +1,20 @@
L111111_S:
IN r4, r0
BRU r4
BLRB_u10 ERROR
BLRF_u10 L000_TX
BLRB_u10 ERROR
BLRB_u10 L0_X
BLRB_u10 L0_X
BLRB_u10 L0_X
BLRB_u10 L0_X
BLRB_u10 ERROR
BLRF_u10 L1_UX
BLRF_u10 L1_UX
BLRB_u10 ERROR
BLRB_u10 L1_UX
BLRF_u10 L11_UX
BLRF_u10 L11_UX
BLRB_u10 L111_UY
BLRB_u10 ERROR

View File

@@ -0,0 +1,20 @@
L111111_U:
IN r4, r0
BRU r4
BLRF_u10 L0000
BLRB_u10 L000
BLRB_u10 ERROR
BLRB_u10 L00
BLRB_u10 ERROR
BLRB_u10 ERROR
BLRB_u10 L0_1
BLRB_u10 ERROR
BLRB_u10 L0001
BLRB_u10 L001
BLRB_u10 ERROR
BLRB_u10 ERROR
BLRB_u10 L0011
BLRF_u10 L011
BLRB_u10 L0111
BLRB_u10 ERROR

View File

@@ -0,0 +1,20 @@
L11111_S:
IN r4, r0
BRU r4
BLRB_u10 ERROR
BLRF_u10 L000_TX
BLRB_u10 ERROR
BLRF_u10 L00_TX
BLRB_u10 ERROR
BLRB_u10 ERROR
BLRB_u10 ERROR
BLRB_u10 L0_TX
BLRF_u10 L1_UX
BLRF_u10 L1_UX
BLRB_u10 ERROR
BLRF_u10 L1_UX
BLRF_u10 L11_UY
BLRB_u10 L11_UY
BLRF_u10 L111_UY
BLRB_u10 ERROR

View File

@@ -0,0 +1,20 @@
L11111_U:
IN r4, r0
BRU r4
BLRB_u10 L0000
BLRB_u10 L000
BLRB_u10 ERROR
BLRB_u10 L00
BLRB_u10 L0_1
BLRB_u10 ERROR
BLRB_u10 L0_1
BLRB_u10 L0
BLRB_u10 L0001
BLRB_u10 L001
BLRB_u10 ERROR
BLRB_u10 L01
BLRB_u10 L0011
BLRF_u10 L011
BLRB_u10 L0111
BLRB_u10 ERROR

View File

@@ -0,0 +1,20 @@
L11111a:
IN r4, r0
BRU r4
BLRF_u10 L0000_S
BLRF_u10 L000_S
BLRF_u10 ERROR
BLRF_u10 L00_S
BLRF_u10 ERROR
BLRF_u10 ERROR
BLRF_u10 ERROR
BLRF_u10 L0_S
BLRF_u10 L1_TZ
BLRF_u10 L1_TZ
BLRB_u10 FASTER
BLRF_u10 L1_TZ
BLRF_u10 L11_TZ
BLRF_u10 L11_TZ
BLRB_u10 L111_TZ
BLRF_u10 ERROR

View File

@@ -0,0 +1,23 @@
.align 4
L1111_S:
.syntax default
bf r6, L1111_0
.syntax architectural
IN r4, r0
BRU r4
BLRB_u10 L000_TY
BLRF_u10 L000_TX
BLRB_u10 ERROR
BLRF_u10 L00_TX
BLRB_u10 ERROR
BLRB_u10 ERROR
BLRB_u10 ERROR
BLRF_u10 L0_TX
BLRF_u10 L1_UY
BLRF_u10 L1_UY
BLRB_u10 ERROR
BLRB_u10 L1_UY
BLRF_u10 L11_UY
BLRF_u10 L11_UY
BLRF_u10 L111_UY
BLRB_u10 L1111111_S

View File

@@ -0,0 +1,31 @@
.align 4
L1111_UY:
BITREV r5, r5
OUT r1,r5
LADD r5,r6,r8,r11,r11
BRFU L1111_U
.align 4
L1111_UZ:
BITREV r5, r5
OUT r1,r5
LADD r5,r6,r8,r9,r11
L1111_U:
IN r4, r0
BRU r4
BLRB_u10 L0000
BLRB_u10 L000
BLRB_u10 ERROR
BLRB_u10 L00
BLRB_u10 L0_1
BLRB_u10 ERROR
BLRB_u10 ERROR
BLRB_u10 L0
BLRB_u10 L0001
BLRB_u10 L001
BLRB_u10 ERROR
BLRB_u10 L01
BLRB_u10 L0011
BLRF_u10 L011
BLRB_u10 L0111
BLRF_u10 L1111111_U

View File

@@ -0,0 +1,23 @@
.align 4
L111_S:
.syntax default
bf r6, L111_0
.syntax architectural
IN r4, r0
BRU r4
BLRB_u10 ERROR
BLRF_u10 L000_TY
BLRB_u10 ERROR
BLRF_u10 L1111100_S
BLRB_u10 ERROR
BLRB_u10 ERROR
BLRB_u10 ERROR
BLRF_u10 L0_TX
BLRF_u10 L1_UY
BLRF_u10 L1_UY
BLRB_u10 ERROR
BLRF_u10 L1_UY
BLRF_u10 L11_UZ
BLRF_u10 L11_UY
BLRF_u10 L111_UZ
BLRF_u10 L1111111_S

View File

@@ -0,0 +1,37 @@
.align 4
L111_TY:
BITREV r5, r5
OUT r1,r5
LADD r5,r6,r8,r11,r11
BRFU L111_T
.align 4
L111_TZ:
BITREV r5, r5
OUT r1,r5
LADD r5,r6,r8,r9,r11
BRFU L111_T
.align 4
L111_TX:
BITREV r5, r5
OUT r1,r5
LSUB r5,r6,r8,r7,r11
L111_T:
IN r4, r0
BRU r4
BLRB_u10 L0000_U
BLRB_u10 L000_U
BLRB_u10 ERROR
BLRB_u10 ERROR
BLRB_u10 L10
BLRB_u10 ERROR
BLRF_u10 L110
BLRB_u10 ERROR
BLRB_u10 L1
BLRB_u10 ERROR
BLRB_u10 ERROR
BLRB_u10 ERROR
BLRB_u10 L11
BLRB_u10 ERROR
BLRF_u10 L111
BLRB_u10 ERROR

View File

@@ -0,0 +1,37 @@
.align 4
L111_UX:
BITREV r5, r5
OUT r1,r5
LSUB r5,r6,r8,r7,r11
BRFU L111_U
.align 4
L111_UY:
BITREV r5, r5
OUT r1,r5
LADD r5,r6,r8,r11,r11
BRFU L111_U
.align 4
L111_UZ:
BITREV r5, r5
OUT r1,r5
LADD r5,r6,r8,r9,r11
L111_U:
IN r4, r0
BRU r4
BLRB_u10 L0000
BLRB_u10 L000
BLRB_u10 ERROR
BLRB_u10 L00
BLRB_u10 ERROR
BLRB_u10 ERROR
BLRB_u10 ERROR
BLRB_u10 L0
BLRB_u10 L0001
BLRB_u10 L001
BLRB_u10 ERROR
BLRB_u10 L01
BLRB_u10 L0011
BLRB_u10 L011
BLRB_u10 L0111
BLRF_u10 L1111111_U

View File

@@ -0,0 +1,23 @@
.align 4
L11_S:
.syntax default
bf r6, L11_0
.syntax architectural
IN r4, r0
BRU r4
BLRB_u10 ERROR
BLRF_u10 L000_TY
BLRB_u10 ERROR
BLRF_u10 L00_TY
BLRB_u10 ERROR
BLRB_u10 ERROR
BLRB_u10 ERROR
BLRF_u10 L111110_S
BLRF_u10 L1_UZ
BLRF_u10 L1_UZ
BLRB_u10 ERROR
BLRF_u10 L1_UY
BLRF_u10 L11_UZ
BLRF_u10 L11_UZ
BLRF_u10 L111_UZ
BLRF_u10 L111111_S

View File

@@ -0,0 +1,38 @@
.align 4
L11_TY:
BITREV r5, r5
OUT r1,r5
LADD r5,r6,r8,r11,r11
BRFU L11_T
.align 4
L11_TZ:
BITREV r5, r5
OUT r1,r5
LADD r5,r6,r8,r9,r11
BRFU L11_T
.align 4
L11_TX:
BITREV r5, r5
OUT r1,r5
LSUB r5,r6,r8,r7,r11
L11_T:
IN r4, r0
BRU r4
BLRB_u10 L0000_U
BLRB_u10 L000_U
BLRB_u10 ERROR
BLRB_u10 L00_U
BLRB_u10 L10
BLRB_u10 ERROR
BLRB_u10 ERROR
BLRB_u10 ERROR
BLRB_u10 L1
BLRB_u10 L1
BLRB_u10 ERROR
BLRB_u10 ERROR
BLRB_u10 L11
BLRB_u10 L11
BLRF_u10 L111
BLRB_u10 ERROR

View File

@@ -0,0 +1,37 @@
.align 4
L11_UX:
BITREV r5, r5
OUT r1,r5
LSUB r5,r6,r8,r7,r11
BRFU L11_U
.align 4
L11_UY:
BITREV r5, r5
OUT r1,r5
LADD r5,r6,r8,r11,r11
BRFU L11_U
.align 4
L11_UZ:
BITREV r5, r5
OUT r1,r5
LADD r5,r6,r8,r9,r11
L11_U:
IN r4, r0
BRU r4
BLRB_u10 L0000
BLRB_u10 L000
BLRB_u10 ERROR
BLRB_u10 L00
BLRB_u10 ERROR
BLRB_u10 ERROR
BLRB_u10 ERROR
BLRB_u10 L0
BLRB_u10 L0001
BLRB_u10 L001
BLRB_u10 ERROR
BLRB_u10 L01
BLRB_u10 L0011
BLRB_u10 L011
BLRB_u10 L0111
BLRF_u10 L111111_U

View File

@@ -0,0 +1,23 @@
.align 4
L1_S:
.syntax default
bf r6, L1_0
.syntax architectural
IN r4, r0
BRU r4
BLRB_u10 ERROR
BLRF_u10 L000_TZ
BLRB_u10 ERROR
BLRF_u10 L11100_S
BLRB_u10 ERROR
BLRB_u10 ERROR
BLRB_u10 ERROR
BLRF_u10 L0_TY
BLRF_u10 L1_UZ
BLRF_u10 L1_UZ
BLRB_u10 ERROR
BLRF_u10 L1_UZ
BLRF_u10 L11_UZ
BLRF_u10 L11_UZ
BLRB_u10 ERROR
BLRF_u10 L11111_S

View File

@@ -0,0 +1,37 @@
.align 4
L1_TZ:
BITREV r5, r5
OUT r1,r5
LADD r5,r6,r8,r9,r11
BRFU L1_T
.align 4
L1_TX:
BITREV r5, r5
OUT r1,r5
LSUB r5,r6,r8,r7,r11
BRFU L1_T
.align 4
L1_TY:
BITREV r5, r5
OUT r1,r5
LADD r5,r6,r8,r11,r11
L1_T:
IN r4, r0
BRU r4
BLRB_u10 L0000_U
BLRB_u10 L000_U
BLRB_u10 ERROR
BLRB_u10 L00_U
BLRB_u10 ERROR
BLRB_u10 ERROR
BLRB_u10 ERROR
BLRB_u10 ERROR
BLRB_u10 L1
BLRB_u10 L1
BLRB_u10 ERROR
BLRB_u10 L1
BLRB_u10 L11
BLRB_u10 L11
BLRB_u10 L111
BLRB_u10 ERROR

View File

@@ -0,0 +1,36 @@
.align 4
L1_UY:
BITREV r5, r5
OUT r1,r5
LADD r5,r6,r8,r11,r11
BRFU L1_U
.align 4
L1_UX:
BITREV r5, r5
OUT r1,r5
LSUB r5,r6,r8,r7,r11
BRFU L1_U
.align 4
L1_UZ:
BITREV r5, r5
OUT r1,r5
LADD r5,r6,r8,r9,r11
L1_U:
IN r4, r0
BRU r4
BLRB_u10 L0000
BLRB_u10 L000
BLRB_u10 ERROR
BLRB_u10 L00
BLRB_u10 ERROR
BLRB_u10 ERROR
BLRB_u10 ERROR
BLRB_u10 L0
BLRB_u10 L0001
BLRB_u10 L001
BLRB_u10 ERROR
BLRB_u10 L01
BLRB_u10 L0011
BLRB_u10 L011
BLRB_u10 L0111
BLRF_u10 L11111_U

View File

@@ -0,0 +1,189 @@
/////////////////////////////////////////////////////////////////////////////
//
// Outputs 32-bit words of data
// Only works down to 396MHz (see explanation below)
//
// This is an SPDIF receiver. It needs 1 thread and no memory other
// than ~2800 bytes of program code. It can do 11025, 12000, 22050, 24000,
// 44100, 48000, 88200, 96000, and 192000.
//
// Its "elegance" is in the implementation method. The signal is sampled at
// 50 Mhz (assuming 192000, which is 12 Mbps), and the bits that come in are
// used to drive a parser. I gratuitously used assembly (all machine generated)
// in order to build a state machine that goes through 5 steps:
// 1) IN 4 seralised bits from the SPDIF PHY
// 2) BRU to these bits (that is, add the value inputted to the program counter)
// 3) BL to the code that deals with the state transition
// 4) Perform an LMUL to add 0/1 bits to the received word
// 5) IN the next 4 serialised bits.
// 6) If an error is encountered, then up a binary counter on the leds and
// record the erroneous part of the stream.
//
// The program needs 40-60 ns to deal with 4 samples, and 80-100 ns to deal
// with the final sample which limits the speed to 12 Mbps or 192,000 samples/s
// stereo. There are around 80 states, each with a 16-long jumptable.
//
// The bit streams are 3.072, 6.144, or 12.288 Mbits/s - we sample them at
// 12.5, 25, or 50 MHz.
// So we get "4.something" samples per bit.
//
// Normally samples 0000/1111 mean '0' and 0011/1100 mean '1'. Any other value
// means 'Error'. However, since it is slightly more than 4, it could be '00000'
// rather than '0000'. Hence, if you sample four bits at a time, you slowly go
// out of sync. The code implements a state machine; state '000' means 'I have
// seen three zeros that I haven't decided what to do with' So if you then
// read samples '0111' then you know that you have seen a logical '0' (0000),
// and you go to state '111' because there are still three ones to deal with.
// So, if your clock is too low, you will sample sometimes only 3 times per bit,
// and you are toast. You can run it at, say, 360 Mhz, but you will have to
// divide the clock by 7 to get a 51.4 MHz clock... Or divide the 90 Mhz clock
// by 3 to get a 30 MHz clock that can sample 96 KHz. There is also a subtlety
// that rise and fall times are not equal, and hence '0111' and '1110' are
// actually ones.
#include <xs1.h>
.syntax architectural
#define STACK 9
.globl SpdifReceive
.linkset SpdifReceive.nstackwords,STACK
.globl SpdifReceive.nstackwords
.linkset SpdifReceive.maxthreads,0
.globl SpdifReceive.maxthreads
.linkset SpdifReceive.maxtimers,0
.globl SpdifReceive.maxtimers
.linkset SpdifReceive.maxchanends,0
.globl SpdifReceive.maxchanends
.globl parseSpDifc
.linkset parseSpDifc.nstackwords,0
.globl parseSpDifc.nstackwords
.cc_top SpdifReceive.func, SpdifReceive
BERROR:
STWSP lr, 8
BLRF parseSpDifE
parseSpDifTerminate:
INCT r11, r1
#ifdef TEST
parseSpDifE:
#endif
CLRSR XS1_SR_EEBLE_MASK
CLRSR XS1_SR_FAST_MASK
EDU r1
OR r0, r4, r4
OR r1, r5, r5
LDWSP r2, 8
LDWSP r4, 1
LDWSP r5, 2
LDWSP r6, 3
LDWSP r7, 4
LDWSP r8, 5
LDWSP r9, 6
LDWSP r10, 7
RETSP STACK
// Parse SPDIF entry point
// arguments:
// void SpdifReceive(
// in buffered port:4 p r0
// streaming chanend c r1
// int initial_divider r2
// clock clk r3
// );
SpdifReceive:
//LDC r4, 0xf
//AND r4, r3, r4
//BRFT r4, parseSpDifc
SETCI r3, 7 // XS1_SETC_RUNR_STOP
SETD r3, r2
SETCLK r0, r3
SETCI r3, 15 // XS1_SETC_RUNR_START
#ifdef INV
SETCI r0, 0x600F // invert
#endif
SETSR XS1_SR_FAST_MASK
parseSpDifc:
ENTSP STACK
STWSP r4, 1
STWSP r5, 2
STWSP r6, 3
STWSP r7, 4
STWSP r8, 5
STWSP r9, 6
STWSP r10, 7
CLRSR XS1_SR_EEBLE_MASK
EDU r1
.syntax default
ldap r11, parseSpDifTerminate
setv res[r1], r11
.syntax architectural
EEU r1
SETSR XS1_SR_EEBLE_MASK
BLRF startParsing
// Entry points for slowing down and speeding up
BFASTER:
#ifndef TEST
GETD r4, r3
SHRI r4, r4, 1
BRFT r4, setClock
BLRF startParsing
#endif
BSLOWER:
#ifndef TEST
GETD r4, r3
SHLI r4, r4, 1
LDC r6, 32
LSU r10, r4, r6
BRFF r10, startParsing
setClock:
SETCI r3, 7
SETD r3, r4
SETCI r3, 15
#endif
BLRF startParsing
startParsing:
LDC r5, 0x5555
LDC r6, 0x0
LDC r7, 0x1
LDC r8, 0x1A
LDC r9, 0x2
LDC r10, 0x4
LDC r11, 0x0
#ifndef TEST
parseSpDifE:
#endif
IN r4, r0
IN r4, r0
SHLI r4, r4, 1
BRU r4
BLRF_lu10 L0000
BLRF_lu10 L000
BLRF_lu10 L00
BLRF_lu10 L00
BLRF_lu10 L0
BLRF_lu10 L0
BLRF_lu10 L0
BLRF_lu10 L0
BLRF_lu10 L1
BLRF_lu10 L1
BLRF_lu10 L1
BLRF_lu10 L1
BLRF_lu10 L11
BLRF_lu10 L11
BLRF_lu10 L111
BLRF_lu10 L1111

View File

@@ -0,0 +1,4 @@
FFASTER: BRBU BFASTER
FSLOWER: BRBU BSLOWER
FERROR: BRBU BERROR
.cc_bottom SpdifReceive.func