Latency or Other Issue?

Post a reply

Confirmation code
Enter the code exactly as it appears. All letters are case insensitive.
Smilies
:D :) ;) :( :o :shock: :? 8-) :lol: :x :P :oops: :cry: :evil: :twisted: :roll: :!: :?: :idea: :arrow: :| :mrgreen: :geek: :ugeek:
BBCode is ON
[img] is ON
[flash] is OFF
[url] is ON
Smilies are ON
Topic review
   

Expand view Topic review: Latency or Other Issue?

Re: Latency or Other Issue?

Post by support »

Hello,

Using a memory interface for transporting streaming data to the FPGA is so wrong, that I find it pointless to try understand what went wrong here specifically.

Yes, indeed, a simple FIFO link is the natural and obvious solution. Just connect a dual-clock FIFO to the Xillybus IP core on one side, and read from it with your DAC logic from the other. You could replace one of the FIFOs in the xillydemo.v example with a dual-clock one, and connect it to your logic.

The dual-clock FIFO handles the dual-clock synchronization for you. If the reading side runs on the sample clock frequency, tie the FIFO's rd_en high, and you'll have audio samples coming out at the right pace.

If a 10 ms latency is an issue to you, please refer to this page:

http://xillybus.com/doc/xillybus-latency

Regards,
Eli

Latency or Other Issue?

Post by Guest »

Ubuntu 16.04
Xilinx AC701
Vivado 2016.2

I am basically trying to create a sound card, but latency seems to be a problem. I am trying do send data at 705,600 Bytes / second (8192 Bytes at 86 Hz intervals). I am using dual port addressable memory. So, in my FPGA code I keep track of the current "period" (8192 Bytes):

always @(posedge ldac_clock) begin
q <= q + 1;

if(q == 0 || q == 256) begin
period <= period + 1;
end
end

The FPGA handles outputting data to the digital to analog converter (DAC) at the required 44.1 kHz clock named ldac_clock. On the FPGA I use enough memory to store 2 periods. One period is being read from, while the other is being written to. When one period is done, it starts reading from the other one automatically (regardless of successful data transfer) and then increments the current_period count. On the C side it sits there within a while loop checking if the current_period is different from the last_read_period. If it is different then it will write the next period to the appropriate space in memory. Either it uses lseek to address 0 or to address 2048 (8192 Bytes / (4 Bytes / address)). So I use 2 different sets of memory. The control_data stores the current period, while dac_data is just that. The main problem is that while the C program is running, I print out the value of the current_period every time it is read and it is consistently missing periods. When it misses so many periods, it kind of makes it useless for my purpose. The idea behind my code is that it flips between the 2 periods back and forth and data can be written to the unused period anytime before the currently used period is used up (I believe this should be considered asynchronous?). But the dac_output side just loops through the chunk of data containing both of the periods independently of everything else going on. Would FIFOs be any better? I read in the documentation that the OS could pause a transfer for tens of milliseconds. Not sure if this is the problem. Right now I have the dac_output code commented out on the FPGA, so I know that is not the issue. I really have no idea sure the issue is coming from. Linux? My code? Hardware? Xillybus?

Any help or suggestions would be greatly appreciated.

always @(posedge bus_clk) begin
if (user_w_dac_data_wren) begin
dac_data[user_dac_data_addr] <= user_w_dac_data_data;
end if (user_r_dac_data_rden) begin
user_r_dac_data_data <= dac_data[user_dac_data_addr];
end

if (user_w_control_data_wren) begin
control_data[user_control_data_addr] <= user_w_control_data_data;
end if (user_r_control_data_rden) begin
//control_data[0] <= {25'd0, q};
user_r_control_data_data <= {8'hAC, 7'b0, period, q}; // control_data[user_control_data_addr];
end
end

assign user_r_dac_data_empty = 0;
assign user_r_dac_data_eof = 0;
assign user_w_dac_data_full = 0;

assign user_r_control_data_empty = 0;
assign user_r_control_data_eof = 0;
assign user_w_control_data_full = 0;

endmodule

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
This code should produce a 86 Hz square wave:

int fd_dac_data, fd_control_data;
int address = 0;
unsigned short old_period = 0;

int main() {
unsigned char ones_data[4096];
unsigned char zero_data[4096];
int i;

for (i = 0; i < 4096; i++) {
ones_data[i] = 0xFF;
}

for (i = 0; i < 4096; i++) {
zero_data[i] = 0x00;
}

setup();

while (1) {
while (output(ones_data));
while (output(zero_data));
}

return 0;
}

void setup() {
int address = 0;
char fd_dd[] = "/dev/xillybus_dac_data";
char fd_cd[] = "/dev/xillybus_control_data";

fd_dac_data = open(fd_dd, O_RDWR);
fd_control_data = open(fd_cd, O_RDWR);

if (fd_dac_data < 0 || fd_control_data < 0) {
printf("Failed to open one of the devfiles.\n");
}

if (lseek(fd_control_data, address, SEEK_SET) < 0) {
perror("Failed to seek");
exit(1);
}
}

int output(unsigned char* dac_data) {
int control = 1;
unsigned char control_data[4];
unsigned short current_period;

if (lseek(fd_control_data, 0, SEEK_SET) < 0) {
perror("Failed to seek");
exit(1);
}

allread(fd_control_data, &control_data[0], 4);
current_period = (unsigned short)((control_data[2] << 8) | control_data[1]);

if (old_period != current_period) {
if (lseek(fd_dac_data, address, SEEK_SET) < 0) {
perror("Failed to seek");
exit(1);
}

allwrite(fd_dac_data, dac_data, 4096);
control = 0;

printf("current_period = %03hu\n", current_period);

if (address == 0) {
//printf("Test01\n");
address = 2048;
}
else {
//printf("Test02\n");
address = 0;
}
}

old_period = current_period;

return control;
}

Top

cron