I managed to get the I2S looback working on my xCore200-MultiChannel board. I also wrote a Ethernet Layer 1 (no mac, no ip) task that sends a 100byte bitstream through the PHY for the receiving party to recognize the audio samples. This for it's own is also working. however, as soon as I combine the loopback with my code things get screwed up and I don't know why. I tried inter-task communication with interface and with streaming channel but they seem to behave as they are synchronous. As soon as I make use of the channel the loopback is stopping and making a high-pitched noise. (clock?)
all I want to do is to let the loopback run, copy the samples to my routine and send the ethernet bitstream along with the loopback. The timing of the Ethernet is maintained correctly but none of my audio samples will arrive on this end.
Also I wonder if I can refactor the sketch to be I2S slave (can the Codecs on the xCORE 200 MultiChannel board generate the MCLK so I can be I2S slave, no samples found for the I2S slave). tile 1 is full because of the rgmii stuff and I'm not 100% sure if the clock blocks on both tiles interfere with each other (hence the I2S slave question).
A simple looback with a copy of the samples to be further processed by another task (with it's own timing) would help me. Many thanks!
Code: Select all
// Copyright (c) 2015-2016, XMOS Ltd, All rights reserved
#include <xs1.h>
#include <platform.h>
#include "ethernet.h"
#include "smi.h"
#include "debug_print.h"
#include <stdio.h>
#include <print.h>
#include "i2s.h"
#include "i2c.h"
#include "gpio.h"
//ETHERNET
#define BASIC_CONTROL_REG 0x0
on tile[1]: rgmii_ports_t rgmii_ports = RGMII_PORTS_INITIALIZER; // Fixed RGMII ports on Tile 1
on tile[1]: port p_smi_mdio = XS1_PORT_1C;
on tile[1]: port p_smi_mdc = XS1_PORT_1D;
on tile[1]: port p_eth_reset = XS1_PORT_4A;
//I2S
/* Ports and clocks used by the application */
on tile[0]: out buffered port:32 p_lrclk = XS1_PORT_1G;
on tile[0]: out buffered port:32 p_bclk = XS1_PORT_1H;
on tile[0]: in port p_mclk = XS1_PORT_1F;
on tile[0]: out buffered port:32 p_dout[4] = {XS1_PORT_1M, XS1_PORT_1N, XS1_PORT_1O, XS1_PORT_1P};
on tile[0]: in buffered port:32 p_din[4] = {XS1_PORT_1I, XS1_PORT_1J, XS1_PORT_1K, XS1_PORT_1L};
on tile[0]: clock mclk = XS1_CLKBLK_1;
on tile[0]: clock bclk = XS1_CLKBLK_2;
on tile[0]: port p_i2c = XS1_PORT_4A;
on tile[0]: port p_gpio = XS1_PORT_8C;
#define SAMPLE_FREQUENCY 48000
#define MASTER_CLOCK_FREQUENCY 24576000
#define CS5368_ADDR 0x4C // I2C address of the CS5368 DAC
#define CS5368_GCTL_MDE 0x01 // I2C mode control register number
#define CS5368_PWR_DN 0x06
#define CS4384_ADDR 0x18 // I2C address of the CS4384 ADC
#define CS4384_MODE_CTRL 0x02 // I2C mode control register number
#define CS4384_PCM_CTRL 0x03 // I2C PCM control register number
typedef interface samples_if {
void buffer(size_t ch, int32_t s);
} samples_if;
//I2S
enum gpio_shared_audio_pins {
GPIO_DAC_RST_N = 1,
GPIO_PLL_SEL = 5, // 1 = CS2100, 0 = Phaselink clock source
GPIO_ADC_RST_N = 6,
GPIO_MCLK_FSEL = 7, // Select frequency on Phaselink clock. 0 = 24.576MHz for 48k, 1 = 22.5792MHz for 44.1k.
};
//ETHERNET
// An enum to manage the array of connections from the ethernet component
// to its clients.
enum eth_clients {
ETH_TO_ICMP,
NUM_ETH_CLIENTS
};
enum cfg_clients {
CFG_TO_ICMP,
CFG_TO_PHY_DRIVER,
NUM_CFG_CLIENTS
};
#define timed_printf \
{ timer t; int u; t :> u; /* GETTIME instruction */ \
printint(u); printstr(" "); \
} printf
void reset_codecs(client i2c_master_if i2c)
{
/* Mode Control 1 (Address: 0x02) */
/* bit[7] : Control Port Enable (CPEN) : Set to 1 for enable
* bit[6] : Freeze controls (FREEZE) : Set to 1 for freeze
* bit[5] : PCM/DSD Selection (DSD/PCM) : Set to 0 for PCM
* bit[4:1] : DAC Pair Disable (DACx_DIS) : All Dac Pairs enabled
* bit[0] : Power Down (PDN) : Powered down
*/
i2c.write_reg(CS4384_ADDR, CS4384_MODE_CTRL, 0b11000001);
/* PCM Control (Address: 0x03) */
/* bit[7:4] : Digital Interface Format (DIF) : 0b1100 for TDM
* bit[3:2] : Reserved
* bit[1:0] : Functional Mode (FM) : 0x11 for auto-speed detect (32 to 200kHz)
*/
i2c.write_reg(CS4384_ADDR, CS4384_PCM_CTRL, 0b00010111);
/* Mode Control 1 (Address: 0x02) */
/* bit[7] : Control Port Enable (CPEN) : Set to 1 for enable
* bit[6] : Freeze controls (FREEZE) : Set to 0 for freeze
* bit[5] : PCM/DSD Selection (DSD/PCM) : Set to 0 for PCM
* bit[4:1] : DAC Pair Disable (DACx_DIS) : All Dac Pairs enabled
* bit[0] : Power Down (PDN) : Not powered down
*/
i2c.write_reg(CS4384_ADDR, CS4384_MODE_CTRL, 0b10000000);
unsigned adc_dif = 0x01; // I2S mode
unsigned adc_mode = 0x03; // Slave mode all speeds
/* Reg 0x01: (GCTL) Global Mode Control Register */
/* Bit[7]: CP-EN: Manages control-port mode
* Bit[6]: CLKMODE: Setting puts part in 384x mode
* Bit[5:4]: MDIV[1:0]: Set to 01 for /2
* Bit[3:2]: DIF[1:0]: Data Format: 0x01 for I2S, 0x02 for TDM
* Bit[1:0]: MODE[1:0]: Mode: 0x11 for slave mode
*/
i2c.write_reg(CS5368_ADDR, CS5368_GCTL_MDE, 0b10010000 | (adc_dif << 2) | adc_mode);
/* Reg 0x06: (PDN) Power Down Register */
/* Bit[7:6]: Reserved
* Bit[5]: PDN-BG: When set, this bit powers-own the bandgap reference
* Bit[4]: PDM-OSC: Controls power to internal oscillator core
* Bit[3:0]: PDN: When any bit is set all clocks going to that channel pair are turned off
*/
i2c.write_reg(CS5368_ADDR, CS5368_PWR_DN, 0b00000000);
}
static char gpio_pin_map[4] = {
GPIO_DAC_RST_N,
GPIO_ADC_RST_N,
GPIO_PLL_SEL,
GPIO_MCLK_FSEL
};
[[combinable]]
void ar8035_phy_driver(client interface smi_if smi,
client interface ethernet_cfg_if eth) {
ethernet_link_state_t link_state = ETHERNET_LINK_DOWN;
ethernet_speed_t link_speed = LINK_100_MBPS_FULL_DUPLEX;
const int phy_reset_delay_ms = 1;
const int link_poll_period_ms = 1000;
const int phy_address = 0x4;
timer tmr;
int t;
tmr :> t;
p_eth_reset <: 0;
delay_milliseconds(phy_reset_delay_ms);
p_eth_reset <: 1;
smi_configure(smi, phy_address, LINK_100_MBPS_FULL_DUPLEX, SMI_DISABLE_AUTONEG);
// LOOPBACK MODE - REMOTE LOOPBACK
/*
// read debug register 0xB
smi.write_reg(phy_address, 0x1D, 0xB);
uint16_t debug_reg_0xb = smi.read_reg(phy_address, 0x1E);
// read debug register 0x11
smi.write_reg(phy_address, 0x1D, 0x11);
uint16_t debug_reg_0x11 = smi.read_reg(phy_address, 0x1E);
// disable hibernate
debug_reg_0xb = debug_reg_0xb & ~ (1 << 0x15);
// enable external loopback
debug_reg_0x11 = debug_reg_0x11 | (1 << 0x0);
// write debug register 0xB
smi.write_reg(phy_address, 0x1D, 0xB);
smi.write_reg(phy_address, 0x1E, debug_reg_0xb);
// write debug register 0x11
smi.write_reg(phy_address, 0x1D, 0x11);
smi.write_reg(phy_address, 0x1E, debug_reg_0x11);
// write control register
smi.write_reg(phy_address, BASIC_CONTROL_REG, 0xA000); // 100Mbits half duplex, remote loopback
*/
while (smi_phy_is_powered_down(smi, phy_address));
while (1) {
select {
case tmr when timerafter(t) :> t:
ethernet_link_state_t new_state = smi_get_link_state(smi, phy_address);
// Read AR8035 status register bits 15:14 to get the current link speed
if (new_state == ETHERNET_LINK_UP) {
link_speed = (ethernet_speed_t)(smi.read_reg(phy_address, 0x11) >> 14) & 3;
}
if (new_state != link_state) {
link_state = new_state;
eth.set_link_state(0, new_state, link_speed);
}
t += link_poll_period_ms * XS1_TIMER_KHZ;
break;
}
}
}
void anet_transmitter(client ethernet_cfg_if cfg,
client ethernet_rx_if rx,
client ethernet_tx_if tx,
streaming chanend c) {
const int packet_period_us = 45; //45 uS each packet
timer tmr;
unsigned t; // changed from int
const int len = 100;
unsigned char txbuf[101] = {0};
txbuf[2] = 0x03; //meta-data for anet stream
for (int i=4;i<=100;i++) {
txbuf[i]=0;
}
timed_printf("Test started\n");
tmr :> t;
t += packet_period_us * XS1_TIMER_MHZ;
while (1)
{
select {
case c :> uint32_t s: //get the sample from the stream channel - prepare txbuf array
//LEFT - odd channels
for (int ch=1;ch<=8;ch++) {
//1st sample buffer
txbuf[(ch-1)*3+4+0] = s>>24 && 0xFF; //bit 16..24
txbuf[(ch-1)*3+4+1] = s>>16 && 0xFF; //bit 08..16
txbuf[(ch-1)*3+4+2] = s>>8 && 0xFF; //bit 00..08
//2nd sample buffer
txbuf[(ch-1)*3+52+0] = s>>24 && 0xFF;
txbuf[(ch-1)*3+52+1] = s>>16 && 0xFF;
txbuf[(ch-1)*3+52+2] = s>>8 && 0xFF;
}
//RIGHT - even channels
for (int ch=9;ch<=16;ch++) {
//1st sample buffer
txbuf[(ch-9)*3+28+0] = s>>24 && 0xFF;
txbuf[(ch-9)*3+28+1] = s>>16 && 0xFF;
txbuf[(ch-9)*3+28+2] = s>>8 && 0xFF;
//2nd sample buffer
txbuf[(ch-9)*3+76+0] = s>>24 && 0xFF;
txbuf[(ch-9)*3+76+1] = s>>16 && 0xFF;
txbuf[(ch-9)*3+76+2] = s>>8 && 0xFF;
}
break;
case tmr when timerafter(t) :> void:
t += packet_period_us * XS1_TIMER_MHZ;
tx.send_packet(txbuf, len, ETHERNET_ALL_INTERFACES);
//timed_printf("A packet was sent\n");
break;
}
}
}
[[distributable]]
void i2s_to_anet(server i2s_callback_if i2s,
client i2c_master_if i2c,
client output_gpio_if dac_reset,
client output_gpio_if adc_reset,
client output_gpio_if pll_select,
client output_gpio_if mclk_select,
streaming chanend c) {
int32_t buff[8] = {0};
while (1)
{
select {
case i2s.init(i2s_config_t &?i2s_config, tdm_config_t &?tdm_config):
i2s_config.mode = I2S_MODE_I2S;
i2s_config.mclk_bclk_ratio = (MASTER_CLOCK_FREQUENCY/SAMPLE_FREQUENCY)/64;
// Set CODECs in reset
dac_reset.output(0);
adc_reset.output(0);
// Select 48Khz family clock (24.576Mhz)
mclk_select.output(1);
pll_select.output(0);
// Allow the clock to settle
delay_milliseconds(2);
// Take CODECs out of reset
dac_reset.output(1);
adc_reset.output(1);
reset_codecs(i2c);
break;
case i2s.receive(size_t index, int32_t sample):
buff[index] = sample;
c <: buff[index]; //send the sample to the stream channel.
/*
* AS SOON AS THE ABOVE LINE IS IN THE I2S LOOPBACK IS SILENT AS IF THE CHANNEL WOULD BE SYNCHRONOUS
*/
break;
case i2s.send(size_t index) -> int32_t sample:
sample = buff[index];
break;
case i2s.restart_check() -> i2s_restart_t restart:
restart = I2S_NO_RESTART;
break;
}
}
}
int main()
{
//Ethernet
ethernet_cfg_if i_cfg[NUM_CFG_CLIENTS];
ethernet_rx_if i_rx[NUM_ETH_CLIENTS];
ethernet_tx_if i_tx[NUM_ETH_CLIENTS];
streaming chan c_rgmii_cfg;
smi_if i_smi;
//I2S
interface i2s_callback_if i_i2s;
interface i2c_master_if i_i2c[1];
interface output_gpio_if i_gpio[4];
streaming chan c; //inter-task communication which carries a sample
par {
//I2S
on tile[0]: {
/* System setup, I2S + Codec control over I2C */
configure_clock_src(mclk, p_mclk);
start_clock(mclk);
i2s_master(i_i2s, p_dout, 4, p_din, 4, p_bclk, p_lrclk, bclk, mclk);
}
on tile[0]: [[distribute]] i2c_master_single_port(i_i2c, 1, p_i2c, 100, 0, 1, 0);
on tile[0]: [[distribute]] output_gpio(i_gpio, 4, p_gpio, gpio_pin_map);
//Ethernet
on tile[1]: rgmii_ethernet_mac(i_rx, NUM_ETH_CLIENTS,
i_tx, NUM_ETH_CLIENTS,
null, null,
c_rgmii_cfg,
rgmii_ports,
ETHERNET_DISABLE_SHAPER);
on tile[1].core[0]: rgmii_ethernet_mac_config(i_cfg, NUM_CFG_CLIENTS, c_rgmii_cfg);
on tile[1].core[0]: ar8035_phy_driver(i_smi, i_cfg[CFG_TO_PHY_DRIVER]);
on tile[1]: smi(i_smi, p_smi_mdio, p_smi_mdc);
//application
on tile[0]: anet_transmitter(i_cfg[CFG_TO_ICMP], i_rx[ETH_TO_ICMP], i_tx[ETH_TO_ICMP], c);
on tile[0]: [[distribute]] i2s_to_anet(i_i2s, i_i2c[0], i_gpio[0], i_gpio[1], i_gpio[2], i_gpio[3], c);
}
return 0;
}