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;
}
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;
}