Migrating from XHRA to XU208

Discussions about USB Audio on XMOS devices
Post Reply
DHembree
Experienced Member
Posts: 75
Joined: Fri Apr 15, 2016 6:46 pm

Post by DHembree »

That's exactly what I'm looking for, and attempted a reference to the applicable define (P_GPIO_MCLK_SEL), but not surprisingly got an error upon build - use of undeclared identifier. I'm sure I'm going to need to include this gpio_defines, but outside of the current app?


DHembree
Experienced Member
Posts: 75
Joined: Fri Apr 15, 2016 6:46 pm

Post by DHembree »

Can you elaborate on this a bit?

Should I place this snippet of code in my defines?

mon2 wrote:For the sake of sanity, best to leave the XMOS code as-is else you will have to continue to make the same changes (if any) anytime the firmware is updated in the future.

Code: Select all

/**
 * @file        port32A.h
 * @brief       Port 32A bit defines for L1 USB Audio board
 * @author      Ross Owen, XMOS Semiconductor
 */

#ifndef _PORT32A_OUT_
#define _PORT32A_OUT_

/* Bit defs */
#define P32A_USB_RST       0x01           /* ULPI rst line */
#define P32A_COD_RST       0x02           /* Codec rst line */
#define P32A_CLK_SEL       0x04           /* MClk Select line */
#define P32A_LED_A         0x08           /* LED A */
#define P32A_LED_B         0x10           /* LED B */

#endif
User avatar
mon2
XCore Legend
Posts: 1913
Joined: Thu Jun 10, 2010 11:43 am
Contact:

Post by mon2 »

No. Just meant that you should not disturb the original XMOS code that tickles the port pins they have already defined. On your XU208, are you making use of all the ports on that device? Maybe you can use an external mux device or similar to multiply the use of the available free ports (just leave these XMOS ports alone). At least till we know where exactly the XMOS IP is making use of these port pins.

Also review gpio.h and see the define for P_F_SELECT

Starting to think that some of these labels are in a witness protection program and their names keep changing from one code run to another...

Code: Select all

#ifndef _GPIO_H_
#define _GPIO_H_
#include "i2c.h"

/* General output port bit definitions (port 4D, PORT_PWR_PLL_MUTE) */
#define P_VA_EN                 0x1    /* 1 = 5V analog and 2v5 ADC digital supply enable */
#define P_PLL_SEL               0x2    /* 1 = CS2100, 0 = Phaselink clock source */
#define P_MUTE_A                0x4    /* Mute A signal - Brought out to header only*/
#define P_MUTE_B                0x8    /* Mute B signal - Brought out to header only*/


/* General output port bit definitions (port 4E, PORT_ADRST_CKSEL_DSD) */
#define P_DAC_RST_N             0x1    /* 0 = Reset ADC */
#define P_ADC_RST_N             0x2    /* 0 = Reset DAC */
#define P_F_SELECT              0x4    /* Select frequency on Phaselink clock. 0 = 24.576MHz for 48k, 1 = 22.5792MHz for 44.1k.*/
#define P_DSD_MODE              0x8    /* DSD mode select 0 = 8i/8o I2S, 1 = 8o DSD*/
DHembree
Experienced Member
Posts: 75
Joined: Fri Apr 15, 2016 6:46 pm

Post by DHembree »

I am already using similar logic to expand the GPIO, and, as I think about it, I could quite easily solve this by using an i2c port expander, but if there is a way to gain access to MCLK_SEL and three other GPIO pins I'm done.

I can reference these defines within my code, correct? Includes? Just the method is all I'm after. Yes, my noobie roots are showing.

Thanks!

Dan
User avatar
akp
XCore Expert
Posts: 578
Joined: Thu Nov 26, 2015 11:47 pm

Post by akp »

Correct me if I'm wrong, but without this feature an existing PCB that supports XHRA won't work without modification? If so then maybe the fix could be added to the "official" unofficial patch.
User avatar
mon2
XCore Legend
Posts: 1913
Joined: Thu Jun 10, 2010 11:43 am
Contact:

Post by mon2 »

Really cannot say for sure for the defines but in general, yes this should be ok. The confusing part at this time of writing is that if you search for "P_F_SELECT", this label is no where to be found inside the many folders. At least that is the result using Windows explorer to search for this keyword.

What is locked down are the bit weights (just like the original XRHA was). That is, GPIO3 = MCLK_SEL but that is how you started this dialog, knowing this :) Sorry. Need to hunt down the code with my sniffing skills will try with some strong coffee later this evening.

@akp
Correct me if I'm wrong, but without this feature an existing PCB that supports XHRA won't work without modification? If so then maybe the fix could be added to the "official" unofficial patch.
You must retain the supplied use of the specific XMOS ports (on the XU208) else a PCB designed for the XRHA will fail to operate correctly / lose features. I think, for now, it is too hairy to remove the port pins used by the XRHA IP and should be considered "reserved" till we all know more about where they are used. Then it should be possible to override such ports to perhaps hard code the IP to suit.
User avatar
akp
XCore Expert
Posts: 578
Joined: Thu Nov 26, 2015 11:47 pm

Post by akp »

Ah, so I am probably wrong and the XMOS code drives pin 38 of the XU208 just like it did on pin 38 of the XHRA. I must have been confused about what DHembree was asking. Carry on, nothing to see here... ;-)
User avatar
mon2
XCore Legend
Posts: 1913
Joined: Thu Jun 10, 2010 11:43 am
Contact:

Post by mon2 »

Found the AudioHwInit routine. Must have been a Windows Explorer issue on why it was not found earlier but review the file named audiohw.xc

Has some very juicy code inside this routine including PLL and clock changes based on mclk:

(snippet copied below)

Code: Select all

//:codec_init
void AudioHwInit(chanend ?c_codec)
{

    unsigned char data[1] = {0};

    i2c_master_init(i2c);

    /* Put DAC in reset. All LEDs off */
    set_gpio((1<<P_GPO_DAC_RST_N), 0);

    // Disable the CLK0 output (to xCORE MCLK in).
    SI5351A_REGWRITE(SI5351A_OE_CTRL, 0xF9);

    // Enable Fanout of MS0 to other outputs.
    SI5351A_REGWRITE(SI5351A_FANOUT_EN, 0xD0);

    // Setup CLK2 output. Sets powered up, integer mode, src PLLB, not inverted, Sel MS0 as src for CLK2 o/p, 4mA drive strength)
    SI5351A_REGWRITE(SI5351A_CLK2_CTRL, 0x69);

    // Change R0 divider to divide by 2 instead of divide by 1. This stays at this value.
    SI5351A_REGWRITE(SI5351A_MS0_R0_DIV, 0x10);

    // Change R2 divider to divide by 8 instead of divide by 1. This will change depending on sample freq.
    SI5351A_REGWRITE(SI5351A_MS2_R2_DIV, 0x30);

    // Now we write the lower bits of Multisynth Parameter P2. This updates all the divider values into the Multisynth block.
    // The other multisynth parameters are correct so no need to write them.
    SI5351A_REGWRITE(SI5351A_MS0_P2_LOWER, 0x00);

    // Wait a bit for Multisynth output to settle.
    delay_milliseconds(1);

    // Enable all the clock outputs now we've finished changing the settings. This will output 24.576MHz on CLK0 to xcore and 6.144MHz to DAC.
    SI5351A_REGWRITE(SI5351A_OE_CTRL, 0xF8);

    /* DAC out of reset */
    set_gpio((1<<P_GPO_DAC_RST_N), 1);

    // Wait a bit for DAC to be ON
    delay_milliseconds(1);

    // Initialise DAC
    SABRE9018Q2C_REGWRITE(SABRE9018Q2C_0E, 0x0A);
    SABRE9018Q2C_REGWRITE(SABRE9018Q2C_2A, 0x25);
    SABRE9018Q2C_REGWRITE(SABRE9018Q2C_01, 0x80);
    SABRE9018Q2C_REGWRITE(SABRE9018Q2C_0A, 0x12);
    SABRE9018Q2C_REGWRITE(SABRE9018Q2C_0F, 0x06);
    SABRE9018Q2C_REGWRITE(SABRE9018Q2C_10, 0x06);
    SABRE9018Q2C_REGWRITE(SABRE9018Q2C_2A, 0x65);
    SABRE9018Q2C_REGWRITE(SABRE9018Q2C_0E, 0x8A);
  
    return;
}
//:

void AudioHwConfig(unsigned samFreq, unsigned mClk, chanend ?c_codec, unsigned dsdMode,
    unsigned samRes_DAC, unsigned samRes_ADC)
{
    unsigned char data[1] = {0};

    SABRE9018Q2C_REGWRITE(SABRE9018Q2C_0E, 0x0A);
    delay_milliseconds(300);
    SABRE9018Q2C_REGWRITE(SABRE9018Q2C_2A, 0x25);

    // Disable the CLK0 and CLK2 outputs while we change them.
    SI5351A_REGWRITE(SI5351A_OE_CTRL, 0xFD);

    /* Sample frequency dependent register settings */
    if ((samFreq % 22050) == 0)
    {
        // MCLK = 22.5792MHz (44.1,88.2,176.4kHz)
        SI5351A_REGWRITE(SI5351A_CLK0_CTRL, 0x6D); // (Sets powered up, integer mode, src PLLB, not inverted, Sel MS0 as src for CLK0 o/p, 4mA drive strength)
        SI5351A_REGWRITE(SI5351A_MS0_P1_UPPER, 0x07); // (Sets relevant bits of P1 divider setting).
    }
    else if ((samFreq % 24000) == 0) 
    {
        // MCLK = 24.576MHz (48,96,192kHz)
        SI5351A_REGWRITE(SI5351A_CLK0_CTRL, 0x4D); // (Sets powered up, integer mode, src PLLA, not inverted, Sel MS0 as src for CLK0 o/p, 4mA drive strength)
        SI5351A_REGWRITE(SI5351A_MS0_P1_UPPER, 0x05); // (Sets relevant bits of P1 divider setting).
    }
    else
    {
        //printintln(samFreq);
        //printstr("Unrecognised sample freq in AudioHwConfig\n");
    }

    // Now we write the lower bits of Multisynth Parameter P2. This updates all the divider values into the Multisynth block.
    SI5351A_REGWRITE(SI5351A_MS0_P2_LOWER, 0x00);

    if (samFreq < 50000) // 44.1, 48kHz.
    {
        SI5351A_REGWRITE(SI5351A_MS2_R2_DIV, 0x30); // Change R2 divider to divide by 8.
    }
    else if (samFreq < 100000) // 88.2, 96kHz.
    {
        SI5351A_REGWRITE(SI5351A_MS2_R2_DIV, 0x20); // Change R2 divider to divide by 4.
    }
    else if (samFreq < 200000) // 176.4, 192kHz.
    {
        SI5351A_REGWRITE(SI5351A_MS2_R2_DIV, 0x10); // Change R2 divider to divide by 2.
    }
    else // 352.8, 384kHz.
    {
        SI5351A_REGWRITE(SI5351A_MS2_R2_DIV, 0x00); // Change R2 divider to divide by 1.
    }

    // Wait a bit for Multisynth output to settle (how long?)
    delay_milliseconds(1);

    // Enable all the clock outputs now we've finished changing the settings.
    SI5351A_REGWRITE(SI5351A_OE_CTRL, 0xF8);

    delay_milliseconds(1);

    if((dsdMode == DSD_MODE_NATIVE) || (dsdMode == DSD_MODE_DOP))
    {
        SABRE9018Q2C_REGWRITE(SABRE9018Q2C_01, 0x83);
    }
    else
    {
        SABRE9018Q2C_REGWRITE(SABRE9018Q2C_01, 0x80);
    }

    SABRE9018Q2C_REGWRITE(SABRE9018Q2C_2A, 0x65);
    SABRE9018Q2C_REGWRITE(SABRE9018Q2C_0E, 0x8A);
}
//:
User avatar
mon2
XCore Legend
Posts: 1913
Joined: Thu Jun 10, 2010 11:43 am
Contact:

Post by mon2 »

akp wrote:Ah, so I am probably wrong and the XMOS code drives pin 38 of the XU208 just like it did on pin 38 of the XHRA. I must have been confused about what DHembree was asking. Carry on, nothing to see here... ;-)
Thought that this is the intent of this project exercise. Since the original XRHA is EOL, you should be ok to drop in the XU208 as a pin for pin replacement and continue to use the PCBs that may be sitting bare on the shelf but now can be stuffed with the XU208 device. So, it is logical to expect the use of the same port pins. Still hunting for that proof but at least we know the bit weights are correct (XU208 bit weights are the same as the XRHA original). That is a very good start.

Now where or where is the code that makes use of these port labels...
User avatar
mon2
XCore Legend
Posts: 1913
Joined: Thu Jun 10, 2010 11:43 am
Contact:

Post by mon2 »

Have a look at the audiohw.xc source code which specifically makes reference to the port for the MCLK_SEL:

Code: Select all

  /* Set master clock select appropriately */
    if ((samFreq % 22050) == 0)
    {
        tmp &= ~P_GPIO_MCLK_SEL;
    }
path is (but do not believe this folder is used for the XU208 but please confirm this):
sw_usb_audio\app_usb_aud_xk_u8_2c\src\extensions

Code: Select all

#include <xs1.h>
#include <platform.h>
#include <xs1_su.h>
#include <print.h>
#include "devicedefines.h"
#include "i2c_shared.h"
#include "gpio_defines.h"
#include "gpio_access.h"
#include "interrupt.h"
#include "dsd_support.h"

/* General Purpose Output port - various output lines such as DAC reset, LEDs etc */
on tile[0] : out port p_gpo   = XS1_PORT_32A;

/* Input port for buttons and switch */
on tile[0] : in port p_sw     = XS1_PORT_4D;


#ifndef IAP
/* If IAP not enabled, i2c ports not declared - still needs for DAC config */
on tile [AUDIO_IO_TILE] : struct r_i2c r_i2c = {PORT_I2C_SCL, PORT_I2C_SDA};
#else
extern struct r_i2c r_i2c;
#endif

#if defined(SW_INT_HANDLER) && defined(IAP) && !defined(USB_SEL_A)
#error not currently supported
#endif

#ifdef SW_INT_HANDLER
#ifndef SWITCH_VAL
#define SWITCH_VAL 0b0000
#endif
#pragma select handler
void handle_switch_request(in port p_sw)
{
    unsigned curSwVal;

    asm("in %0, res[%1]":"=r"(curSwVal):"r"(p_sw));
    asm("setd res[%0], %1"::"r"(p_sw),"r"(curSwVal));

    if((curSwVal & 0b1000) != SWITCH_VAL)
    {
        /* Reset U8 device */
        write_node_config_reg(usb_tile, XS1_SU_CFG_RST_MISC_NUM, 1);
    }

}
#endif

#ifdef USB_SEL_A
#define USB_SEL_VAL P_GPIO_USB_SEL1
#else
#define USB_SEL_VAL 0
#endif

//:codec_init
void AudioHwInit(chanend ?c_codec)
{
    unsigned x;
#ifdef SW_INT_HANDLER
    unsigned curSwVal;
    timer t;
    unsigned time;
#endif

    port32A_lock_peek(x);

#ifndef IAP
    /* P_GPIO_VBUS_OUT_EN is pulled up, drive low to disable in non MFi builds */
    x&= ~P_GPIO_VBUS_OUT_EN;
#endif
    x |= (P_GPIO_5VA_EN | P_GPIO_SS_EN_CTRL | USB_SEL_VAL);

    port32A_out_unlock(x);

    /* The 5VA_EN line has a cap on it, wait for it to go high */
    port32A_lock();
    while(1)
    {
        x = peek(p_gpo);
        if((x & P_GPIO_5VA_EN) == P_GPIO_5VA_EN)
            break;
    }
    port32A_unlock();

#ifdef SW_INT_HANDLER
     /* Give some time for button debounce */
    t :> time;
    t when timerafter(time+10000000):> void;

    p_sw :> curSwVal;
    curSwVal = (curSwVal & 0b0111) | SWITCH_VAL; // Ensure we don't miss any switches that happened during reboot

    asm("setc res[%0], %1"::"r"(p_sw),"r"(XS1_SETC_COND_NEQ));
    asm("setd res[%0], %1"::"r"(p_sw),"r"(curSwVal));

    set_interrupt_handler(handle_switch_request, 1, p_sw, 0)
#endif
    return;
}
//:


/* I2C address of CS4392 DAC */
#define DAC_I2C_DEV_ADDR               (0x20>>1)

/* Mode Control 1 Register - Address 0x01 */
#define DAC_REG_ADDR_MODE_CTRL1        0x01

/* Volume and Mixing Control - Address 0x02 */
#define DAC_REG_ADDR_VOLMIX_CTRL       0x02

/* Channel A Volume Control - Address 0x03 */
#define DAC_REG_ADDR_A_VOL             0x03

/* Channel B Volume Control - Address 0x04 */
#define DAC_REG_ADDR_B_VOL             0x04

/* Mode Control 2 - Address 0x05 */
#define DAC_REG_ADDR_MODE_CTRL2        0x05

#define DAC_REG_MODE_CTRL2_MCLKDIV2    0x02
#define DAC_REG_MODE_CTRL2_FREEZE      0x04
#define DAC_REG_MODE_CTRL2_MUTECAB     0x08
#define DAC_REG_MODE_CTRL2_PDN         0x10
#define DAC_REG_MODE_CTRL2_CPEN        0x20
#define DAC_REG_MODE_CTRL2_INVERTB     0x40
#define DAC_REG_MODE_CTRL2_INVERTA     0x80

/* Mode Control 3 - Address 0x06 */
#define DAC_REG_ADDR_MODE_CTRL3        0x06

#define DAC_REGWRITE(reg, val) {data[0] = val; i2c_shared_master_write_reg(r_i2c, DAC_I2C_DEV_ADDR, reg, data, 1);}

#define DAC_REGREAD(reg, val)  {i2c_shared_master_read_reg(r_i2c, DAC_I2C_DEV_ADDR, reg, val, 1);}

//:codec_config
/* Called on a sample frequency change */
/* Configure the CS4392 DAC
 * Note the CS5340 ADC doesn't require any config - it is set to Slave mode in hardware (M0 & M1 pins high)
 */
void AudioHwConfig(unsigned samFreq, unsigned mClk, chanend ?c_codec, unsigned dsdMode,
    unsigned samRes_DAC, unsigned samRes_ADC)
{
    timer t;
    unsigned time;
    unsigned tmp;
    unsigned char data[] = {0, 0};


    port32A_lock_peek(tmp);

    /* Put DAC and ADC into reset */
    tmp &= (~P_GPIO_RST_DAC);
    tmp &= (~P_GPIO_RST_ADC);

    /* Set master clock select appropriately */
    if ((samFreq % 22050) == 0)
    {
        tmp &= ~P_GPIO_MCLK_SEL;
    }
    else //if((samFreq % 24000) == 0)
    {
        tmp |= P_GPIO_MCLK_SEL;
    }

    /* Output to port */
    port32A_out_unlock(tmp);

    /* Hold in reset for 2ms while waiting for MCLK to stabilise */
    t :> time;
    time += 200000;
    t when timerafter(time) :> int _;

    /* ADC and DAC out of Reset */
    port32A_lock_peek(tmp);

#if (DSD_CHANS_DAC > 0)
    if(dsdMode)
    {
        /* Set DSD mux line high */
        tmp |= (P_GPIO_DSD_EN);
    }
    else
    {
        tmp &= (~P_GPIO_DSD_EN);
    }
#else
    tmp &= (~P_GPIO_DSD_EN);
#endif

    /* ADC and DAC out of Reset */
    tmp |= (P_GPIO_RST_DAC | P_GPIO_RST_ADC);

    port32A_out_unlock(tmp);

    /* Give the DAC a little time to settle down after reset */
    t :> time;
    time += 200000;
    t when timerafter(time) :> int _;

    /* Set power down (PDN) bit and Control Port Enable bit in DAC */
    DAC_REGWRITE(DAC_REG_ADDR_MODE_CTRL2, DAC_REG_MODE_CTRL2_CPEN | DAC_REG_MODE_CTRL2_PDN);

    /* Mode Control 1
     * 0:1: Functional Mode
     * 2:3: De-emphasis Control
     * 4:6: Digital Interface Formats
     * 7:   Auto-mute
    */
#if (DSD_CHANS_DAC > 0)
    if((dsdMode == DSD_MODE_NATIVE) || (dsdMode == DSD_MODE_DOP))
    {
        if(samFreq < 3000000) /* < 3MHz e.g. 2.2822400 MHz */
        {
            /* 64x oversampled DSD with 8 x DSD clock to mclk */
            DAC_REGWRITE(DAC_REG_ADDR_MODE_CTRL1, 0b00100011);
        }
        else
        {
            /* 128x oversampled DSD with 4 x DSD clock to mclk */
            DAC_REGWRITE(DAC_REG_ADDR_MODE_CTRL1, 0b01100011);
        }
    }
    else
#endif
    {
        if(samFreq < 100000)
        {
            /* Auto-mute off, i2s, Single-speed */
            DAC_REGWRITE(DAC_REG_ADDR_MODE_CTRL1, 0b00010000);
        }
        else
        {
            /* Auto-mute off, i2s, Double-speed */
            DAC_REGWRITE(DAC_REG_ADDR_MODE_CTRL1, 0b00010010);
        }
    }

    /* Volume and Mixing Control
     * 0:   ATAPI 0
     * 1:   ATAPI 1
     * 2:   ATAPI 2
     * 3:   ATAPI 3
     * 4:   ATAPI 4
     * 5:   Zero Cross
     * 6:   Soft Ramp
     * 7:   A vol = B vol
     */
    DAC_REGWRITE(DAC_REG_ADDR_VOLMIX_CTRL, 0b01101001);

    /* Channel A/B Volume Control
     * 0:6: Vol0:Vol6 (Attenutation from 0 to -127db)
     * 7:   Mute
     */
    DAC_REGWRITE(DAC_REG_ADDR_A_VOL, 0b00000000);
    DAC_REGWRITE(DAC_REG_ADDR_B_VOL, 0b00000000);

    /* Mode Control 2
     * 0:1: Reserved
     * 2:   Soft Volume Ramp-up after Reset
     * 3:   Soft Volume Ramp-down after Reset
     * 4:   Interperlolation Filter Select (0 fast or 1 slow roll-off)
     * 5:7: Reserved
     */
    DAC_REGWRITE(DAC_REG_ADDR_MODE_CTRL3, 0b00000000);


#ifdef CODEC_MASTER
#error not currently implemented
#endif

    /* Clear power down bit in the DAC - keep control port enabled for now */
    DAC_REGWRITE(DAC_REG_ADDR_MODE_CTRL2, DAC_REG_MODE_CTRL2_CPEN);
}
//:

try to make some code changes to the above folders to confirm which path is used for this XRHA to XU208 conversion.

----

Late in the day but please also confirm the XRHA schematics and check what is the label on GPIO3 on the original = DAC_RST_N so this has nothing to do with MCLK_SEL? This may make sense since there is a PLL onboard with 3 outputs. Using I2C, any clock can be generated. Does not require additional port pins. Strange...let me know if you disagree.


Image
Post Reply