Use 4-bit port for S/PDIF TX/RX

If you have a simple question and just want an answer.
ffomich
Experienced Member
Posts: 119
Joined: Mon Sep 15, 2014 1:32 pm

Use 4-bit port for S/PDIF TX/RX

Post by ffomich »

Hi,

does anybody try to use one pin of 4-bit port for transmit/receive S/PDIF stream?

I have tested app_example_tx from sc_spdif-1.3.4alpha0.

When I output S/PDIF stream to 1-bit port (as described in module documentation) it works correctly. Output frequency is about 1.778 MHz.

When I output S/PDIF stream to pin 0 of 4-bit port (3 other pins are not connected on board) output frequency is about 7.060 MHz and there is SYNC ERROR on the receiver.


henk
Respected Member
Posts: 347
Joined: Wed Jan 27, 2016 5:21 pm

Post by henk »

Hi,

You won't be able to use it for receiving S/PDIF - the receiver relies on the serialiser on the 1-bit port.

For output you may be able to use a 4-bit port, but you would have to spread out the tables so that it runs at a quarter of the speed. As it is, you will be sending every fourth bit to pin 0, bits (4n+1) to pin 1, etc.

Cheers,
Henk
ffomich
Experienced Member
Posts: 119
Joined: Mon Sep 15, 2014 1:32 pm

Post by ffomich »

Henk,
as I understand:

If output port is 1-bit port.

Code: Select all

out buffered port:32 p = XS1_PORT_1O;
unsigned data = 0b[b31 b30 ... b1 b0], b0-b31 - bits of 'data' value

p <: data;

port output = b0 b1 b2 b3 b4 ... b31
If output port is 4-bit port, for example, P4D.

Code: Select all

out buffered port:32 p = XS1_PORT_4D;
unsigned data = 0b[b31 b30 ... b1 b0], b0-b31 - bits of 'data' value

p <: data;

P4D0 output = b0 b4 b8 ... b28
P4D1 output = b1 b5 b9 ... b29
P4D2 output = b2 b6 b10... b30
P4D3 output = b3 b7 b11... b31

So, to output all 'data' bits only to P4D0, 'data' must be

Code: Select all

unsigned data0 = 0b[b7 0 0 0 b6 0 0 0 b5 0 0 0 ... b0 0 0 0]
unsigned data1 = 0b[b15 0 0 0 b14 0 0 0 b13 0 0 0 ... b8 0 0 0]
unsigned data2 = 0b[b23 0 0 0 b22 0 0 0 b21 0 0 0 ... b16 0 0 0]
unsigned data3 = 0b[b31 0 0 0 b30 0 0 0 b29 0 0 0 ... b24 0 0 0]

p <: data0;
p <: data1;
p <: data2;
p <: data3;

P4D0 output = b0 b1 b2 ... b31
P4D1 output = 0 0 0 ... 0
P4D2 output = 0 0 0 ... 0
P4D3 output = 0 0 0 ... 0
Is my reasoning correct?
henk
Respected Member
Posts: 347
Joined: Wed Jan 27, 2016 5:21 pm

Post by henk »

Yes - completely correct.

If you use xCORE200 you can use the zip instruction twice to do this.

Zip (b31 b30 b29..b0) with (0 0 0 .. 0) and you get two words (b31 0 b30 0 ... b16 0) and (b15 0 b14 0 b13 0 ... b0 0); do this once more and you get four words (b31 0 0 0 b30 0 0 0) etc.

Even though this is only two instructions, it very much unbalances your rhythm of IO and computation.

However, if you have a spare logical core in your system, just make the SPDIF TX thread output to a channel, and write a single while loop that reads a word, splits it into four words using the ZIP instructions, and output the four words.

Cheers,
Henk
ffomich
Experienced Member
Posts: 119
Joined: Mon Sep 15, 2014 1:32 pm

Post by ffomich »

Henk,
I try to use ZIP instruction, but I get strange result.

I run program in simulator and watch values in debugger

Code: Select all

    unsigned h, l;
    h = 0xFFFFFFFF;
    l = 0;

    asm ("zip %0 , %1 , 4" : "=r"(h) , "=r"(l) : "r"(h) , "r"(l)); // zip with 2^4=16 bit step
// I expect to see h = 11..1100..00, l = 11..1100..00, but h = 00..0011..11, l = 00..0011..11
The same is with other steps

Code: Select all

    unsigned h, l;
    h = 0xFFFFFFFF;
    l = 0;

    asm ("zip %0 , %1 , 3" : "=r"(h) , "=r"(l) : "r"(h) , "r"(l)); // zip with 2^3=8 bit step
// h = l = 0..01..10..01..10..01..10..01..1

Code: Select all

    unsigned h, l;
    h = 0xFFFFFFFF;
    l = 0;

    asm ("zip %0 , %1 , 0" : "=r"(h) , "=r"(l) : "r"(h) , "r"(l)); // zip with 2^0=1 bit step
// h = l = 01..01
henk
Respected Member
Posts: 347
Joined: Wed Jan 27, 2016 5:21 pm

Post by henk »

I am wondering whether something funky happened with the asm

Try using these:

Code: Select all

/**
 * Unzip a 64-bit value into two 32-bit values, with a granularity of
 * bits, bit pairs, nibbles, bytes or byte pairs.
 * \param value The 64-bit zipped value.
 * \param log_granularity The logarithm of the granularity.
 * \return Two 32-bit unzipped values.
 */
{unsigned int, unsigned int} unzip(unsigned long long value,
                                   unsigned int log_granularity);

/**
 * Zip two 32-bit values into a single 64-bit value, with a granularity of
 * bits, bit pairs, nibbles, bytes or byte pairs.
 * \param value1 The first 32-bit value.
 * \param value2 The second 32-bit value.
 * \param log_granularity The logarithm of the granularity.
 * \return The 64-bit zipped value.
 */
unsigned long long zip(unsigned int value1, unsigned int value2,
                       unsigned int log_granularity);
(from xs1.h)
ffomich
Experienced Member
Posts: 119
Joined: Mon Sep 15, 2014 1:32 pm

Post by ffomich »

It works correctly.
ffomich
Experienced Member
Posts: 119
Joined: Mon Sep 15, 2014 1:32 pm

Post by ffomich »

I have found a mistake in one of my previous post
So, to output all 'data' bits only to P4D0, 'data' must be

Code: Select all

unsigned data0 = 0b[b7 0 0 0 b6 0 0 0 b5 0 0 0 ... b0 0 0 0]
unsigned data1 = 0b[b15 0 0 0 b14 0 0 0 b13 0 0 0 ... b8 0 0 0]
unsigned data2 = 0b[b23 0 0 0 b22 0 0 0 b21 0 0 0 ... b16 0 0 0]
unsigned data3 = 0b[b31 0 0 0 b30 0 0 0 b29 0 0 0 ... b24 0 0 0]

p <: data0;
p <: data1;
p <: data2;
p <: data3;

P4D0 output = b0 b1 b2 ... b31
P4D1 output = 0 0 0 ... 0
P4D2 output = 0 0 0 ... 0
P4D3 output = 0 0 0 ... 0
In that case output "b0 b1 b2 ... b31" will be on P4D3 pin.

For P4D0 pin output correct values are:

Code: Select all

unsigned data0 = 0b[0 0 0 b7 0 0 0  b6 0 0 0 ... 0 0 0 b0]
unsigned data1 = 0b[0 0 0 b15 0 0 0 b14 0 0 0 ... 0 0 0 b8]
unsigned data2 = 0b[0 0 0 b23 0 0 0 b22  0 0 0 ... 0 0 0 b16]
unsigned data3 = 0b[0 0 0 b31 0 0 0 b30  0 0 0 ... 0 0 0 b24]
ffomich
Experienced Member
Posts: 119
Joined: Mon Sep 15, 2014 1:32 pm

Post by ffomich »

I replace all partout() function calls in SpdifTransmit.xc file with calls to function my_partout()

Code: Select all

#include <xs1.h>

void my_partout(out buffered port:32 p, unsigned n, unsigned val)
{
    if (n == 16)
        // send 16 bits to port in 2 steps: insert zeroes in each 8-bit part twice and send 32 bit to port, repeat
    {
       unsigned long long res = 0;      // 64-bit value
       unsigned int data = val & 0xFF;  // get low 8-bit = [x x x x ... b7 b6 ... b0]
       res = zip(0, data, 0);   // convert to [0 x 0 x ... b7 0 b6 0 ... 0 b0]
       res &= 0xFFFFFFFF;       // get low 32-bit part

       data = zip(0, res, 0);   // convert again to [0 0 0 x 0 0 0 x ... b7 0 0 0 b6 ... 0 0 0 b0]
       partout(p, 32, data);    // send low 32 bits to port [0 0 0 b7 0 0 0 b6 ... 0 0 0 b0]


       data = (val >> 8) & 0xFF; //get bits from 15 to 8 = [x x x x ... b15 b14 ... b8]
       res = zip(0, data, 0);    // convert to [0 x 0 x ... b15 0 b14 0 ... 0 b8]
       res &= 0xFFFFFFFF;        // get low 32-bit part

       data = zip(0, res, 0);   // convert again to [0 0 0 x 0 0 0 x ... b15 0 0 0 b14 ... 0 0 0 b8]
       partout(p, 32, data);    // send low 32 bits to port [0 0 0 b15 0 0 0 b14 ... 0 0 0 b8]

    }
    else if (n == 8)
        // send 8 bits to port in 1 step: insert zeroes twice and send 32 bit to port
    {
        unsigned long long res = 0;
        unsigned int data = val & 0xFF;
        res = zip(0, data, 0);

        res &= 0xFFFFFFFF;

        data = zip(0, res, 0);
        partout(p, 32, data);
    }
}
It seems it work!
henk
Respected Member
Posts: 347
Joined: Wed Jan 27, 2016 5:21 pm

Post by henk »

Great.

I suggest to replace the remaining calls to partout(..., 32, ...) with ordinary <:

Partout is designed to do a partial output, but it limits buffering.
<: is guaranteed to make the most of buffering.

Cheers,
Henk