so far I could not find a code example or a question in the forum, how to receive data from a port with a timeout and a bit width of 16, 24 or 32 bit under the condition, that the bit width is configurable and can change during runtime. BTW, this may be also interesting to those who want to receive different bit widths over I2S in slave mode. Therefore I asked here: "https://www.xcore.com/viewtopic.php?f=26&t=6855", how to realize this with the function partin(). But I gave myself the answer, why I guess, it is not possible.
Now, I have found an alternative (test) implementation and want to share my idea with you, hopefully it interests you and is useful to you. I have pasted my code below. I am not sure, if it is fast and efficient enough, therefore Ideas for improvement are welcome -- also alternative implementations. I have written following code for the multi audio dev board. You should even test the code with the startkit board. The ports XS1_PORT_1O and XS1_PORT_1P and also the ports XS1_PORT_1M and XS1_PORT_1N needs to be linked with a jumper.
How does the code work:
- The producer_task writes out the magic sequence "0xF0A05030" on port XS1_PORT_1N.
- The clock_task produces a very slow clock signal (200ms = 1 period) and output the signal on port XS1_PORT_1O. The clock signal is joined to the clock block named "clk_out" and pulses the output port XS1_PORT_1N. This is configured in the function init().
- The consumer_task reads the data from the port XS1_PORT_1M. The port is clocked by the clock block "clk_in". The "clk_in" is managed by the input port XS1_PORT_1P. This configuration is done also in the init() function. The consumer_task is configurable, how many bytes should be received to realize a bit widths from 16 to 32 bit. The trick is to buffer the data in the port XS1_PORT_1M only with 8 bits. The counter checks, how many bytes must be read to fit the bit width. The rest is only timeout handling and reseting the state engine (counter/value).
- If you interrupt the clock connection (XS1_PORT_1O <-> XS1_PORT_1P), you can test the timeout mechanism.
Code: Select all
#include <platform.h>
#include <xs1.h>
#include <stdio.h>
#include <stdint.h>
#include <xclib.h>
#define DELAY_TICKS 10000000
#define JITTER_TOLERANCE 10000
#define SEQUENCE 0xF0A05030
on tile[0]: clock clk_out = XS1_CLKBLK_1;
on tile[0]: clock clk_in = XS1_CLKBLK_2;
// Join following ports with a jumper
on tile[0]: out port p_clk_out = XS1_PORT_1O;
on tile[0]: out port p_clk_in = XS1_PORT_1P;
// Join following ports with a jumper
on tile[0]: in buffered port:8 p_in = XS1_PORT_1M;
on tile[0]: out buffered port:32 p_out = XS1_PORT_1N;
void producer_task(unsigned count) {
printf("Please wait\n");
for (;;) {
printf("out: %x\n", SEQUENCE);
p_out <: SEQUENCE;
}
}
// Read a port with timeout; this makes it possible to add further case blocks i.e.
// to reconfigure something
void consumer_task(unsigned count) {
unsigned part, value, time, counter;
timer t;
counter = 0;
t :> time;
time += (16 * count) * DELAY_TICKS + JITTER_TOLERANCE; // "16 *" is required, because one clock cycle is "2 * DELAY_TICKS" and we receive 8 bits => 2 * 8 = 16
// Why is this necessary?
clearbuf(p_in);
for (;;) {
select {
case p_in :> part :
// Stick 8bit parts together
value |= (part << (8 * counter));
// (Count * 8) number of bit read?
if (counter + 1 >= count) {
// Reset timer; to avoid time drift effect read out the current time
t :> time;
time += (16 * count) * DELAY_TICKS + JITTER_TOLERANCE;
// Processing data like wring to channel; the test does only printed out
printf("in: %x\n", value);
// Reset state engine
counter = 0;
value = 0;
} else {
counter++;
}
break;
case t when timerafter(time) :> void :
// This should only happen, if the jumper between XS1_PORT_1O and XS1_PORT_1P is removed
printf("Timeout\n");
// Reset state engine
counter = 0;
value = 0;
// Reset timer; to avoid time drift effect read out the current time
t :> time;
time += (16 * count) * DELAY_TICKS + JITTER_TOLERANCE;
break;
}
}
}
void clock_task() {
unsigned value = 1;
for (;;) {
p_clk_out <: value;
value = (value ^1) & 1;
delay_ticks(DELAY_TICKS);
}
}
void init(unsigned count) {
configure_clock_src(clk_out, p_clk_out);
configure_out_port_no_ready(p_out, clk_out, 0);
configure_clock_src(clk_in, p_clk_in);
configure_in_port_no_ready(p_in, clk_in);
start_clock(clk_in);
start_clock(clk_out);
par {
consumer_task(count);
producer_task(count);
clock_task();
}
}
int main(void) {
par {
on tile[0] : init(3); // 1 = 8bit, 2 = 16bit, 3 = 24bit, 4 = 32bit
}
return 0;
}
- There is no sync between input and output. Therefore, if you interrupt the clock connection, the bit pattern between in and out may be shifted.
- Changing bit width during runtime.