2

I'm developing on an AD Blackfin BF537 DSP running uClinux. I have a total of 32MB SD-RAM available. I have an ADC attached, which I can access using a simple, blocking call to read().

The most interesting part of my code is below. Running the program seems to work just fine, I get a nice data package that I can fetch from the SD-card and plot. However, if I comment out the float calculation part (as noted in the code), I get only zeroes in the ft_all.raw file. The same occurs if I change optimization level from -O3 to -O0.

I've tried countless combinations of all sorts of things, and sometimes it works, sometimes it does not - earlier (with minor modifications to below), the code would only work when optimization was disabled. It may also break if I add something else further down in the file.

My suspicion is that the data transferred by the read()-function may not have been transferred fully (is that possible, even though it returns the correct number of bytes?). This is also the first time I initialize pointers using direct memory adresses, and I have no idea how the compiler reacts to this - perhaps I missed something, here?

I've spent days on this issue now, and I'm getting desperate - I would really appreciate some help on this one! Thanks in advance.

// Clear the top 16M memory for data processing
memset((int *)0x01000000,0x0000,(size_t)SIZE_16M);

/* Prep some pointers for data processing */
int16_t *buffer;
int16_t *buf16I, *buf16Q;

buffer = (int16_t *)(0x1000000);
buf16I = (int16_t *)(0x1600000);
buf16Q = (int16_t *)(0x1680000);

/* Read data from ADC */
int rbytes = read(Sportfd, (int16_t*)buffer, 0x200000);
if (rbytes != 0x200000) {
    printf("could not sample data! %X\n",rbytes);
    goto end;
} else {
    printf("Read %X bytes\n",rbytes);
}

FILE *outfd;
int wbytes;

/* Commenting this region results in all zeroes in ft_all.raw */
float a,b;
int c;
b = 0;
for (c = 0; c < 1000; c++) {
    a = c;
    b = b+pow(a,3);
}
printf("b is %.2f\n",b);

/* Only 12 LSBs of each 32-bit word is actual data.
 * First 20 bits of nothing, then 12 bits I, then 20 bits
 * nothing, then 12 bits Q, etc...
 * Below, the I and Q parts are scaled with a factor of 16
 * and extracted to buf16I and buf16Q.
 * */
int32_t *buf32;
buf32  = (int32_t *)buffer;
uint32_t i = 0;
uint32_t n = 0;
while (n < 0x80000) {
    buf16I[i] = buf32[n] << 4;
    n++;
    buf16Q[i] = buf32[n] << 4;
    i++;
    n++;
}

printf("Saving to /mnt/sd/d/ft_all.raw...");
outfd = fopen("/mnt/sd/d/ft_all.raw", "w+");
if (outfd == NULL) {
    printf("Could not open file.\n");
}
wbytes = fwrite((int*)0x1600000, 1, 0x100000, outfd);
fclose(outfd);
if (wbytes < 0x100000) {
    printf("wbytes not correct (= %d) \n", (int)wbytes);

}
printf(" done.\n");

Edit: The code seems to work perfectly well if I use read() to read data from a simple file rather than the ADC. This leads me to believe that the rather hacky-looking code when extracting the I and Q parts of the input is working as intended. Inspecting the assembly generated by the compiler confirms this.

I'm trying to get in touch with the developer of the ADC driver to see if he has an explanation of this behaviour.

The ADC is connected through a SPORT, and is opened as such:

sportfd = open("/dev/sport1", O_RDWR);
ioctl(sportfd, SPORT_IOC_CONFIG, spconf);

And here are the options used when configuring the SPORT:

spconf->int_clk     = 1;
spconf->word_len    = 32;
spconf->serial_clk  = SPORT_CLK;
spconf->fsync_clk   = SPORT_CLK/34;
spconf->fsync       = 1;
spconf->late_fsync  = 1;
spconf->act_low     = 1;
spconf->dma_enabled = 1;
spconf->tckfe       = 0;
spconf->rckfe       = 1;
spconf->txse        = 0;
spconf->rxse        = 1;

A bfin_sport.h file from Analog Devices is also included: https://gist.github.com/tausen/5516954

Update After a long night of debugging with the previous developer on the project, it turned out the issue was not related to the code shown above at all. As Chris suggested, it was indeed an issue with the SPORT driver and the ADC configuration.

While debugging, this error messaged appeared whenever the data was "broken": bfin_sport: sport ffc00900 status error: TUVF. While this doesn't make much sense in the application, it was clear from printing the data, that something was out of sync: the data in buffer was on the form 0x12000000,0x34000000,... rather than 0x00000012,0x00000034,... whenever the status error was shown. It seems clear then, why buf16I and buf16Q only contained zeroes (since I am extracting the 12 LSBs).

Putting in a few calls to usleep() between stages of ADC initialization and configuration seems to have fixed the issue - I'm hoping it stays that way!

Tausen
  • 55
  • 7
  • Reduce this to a minimal testcase, and I'm certain you'll find your problem. If not, post your minimal, **compilable** testcase here and we'll help you through it. – autistic May 02 '13 at 22:55
  • FYI this is a 16bit fixed point DSP running **uClinux** *without a memory management unit*. All processes and the kernel share a single address space. malloc() exists, but without an MMU to join physically disparate blocks into a contiguous logical region, it can eventually fail from fragmentation. It's common to tell the kernel not to use all the system RAM and to manually allocate larger DSP buffers in the region so reserved. So while there is a Linux aspects, there are also concerns resembling bare-metal microcontroller work. – Chris Stratton May 03 '13 at 13:19
  • Do you have a link to the ADC driver source? It's probably a derivation of bfin_sport.c but the details would be important. – Chris Stratton May 03 '13 at 18:11
  • While I'm not convinced that it should be necessary, something that could be worth trying would be to declare the pointers as "volatile int16_t *" and similar, and/or use the pointer rather the constant again in the write() call. Additionally, you could try pre-filling the memory with a recognizable value before the read() call, and seeing if (the shifted and selected bits version of) that ends up in your file instead of 0's. – Chris Stratton May 04 '13 at 00:14
  • I'm sorry for the delay, and thank you very much for your feedback! I'm trying to get ahold of some more driver-code, will post asap. I'll add a note on the adc in the OP, and will try your suggestion and report back. Thanks again. – Tausen May 04 '13 at 09:17
  • @ChrisStratton it was indeed a SPORT driver / ADC configuration issue, as described in the update to the OP. Thanks for the advice. – Tausen May 08 '13 at 09:03
  • Glad you are making progress. Somewhat surprising you are getting a transmit underflow error - could you explain the nature of the connection to the ADC, and how its clock and framing signals are generated? Incidentally, it's possible there's a configuration of the SPORT word width and frame delay which would capture the data to memory in the 12-bits-within-16 format you need, avoiding the need for translation. – Chris Stratton May 08 '13 at 15:23
  • That sounds very interesting! I only have limited information without first consulting the devs or looking up the schematics, but the ADC is an [AD7262](http://www.analog.com/static/imported-files/data_sheets/AD7262.pdf). The serial clock out signal from the Blackfin is connected to the ADC **and** the serial clock in pin on the Blackfin itself. To get "16" bits of data from the ADC, the bfin must send 32 clocks (thus receiving 16 bits of zeros, then "16" bits of data). At least that is what I heard. – Tausen May 08 '13 at 20:09
  • Maybe it's having to send fake data while it generates the clocks, and the TUVF is because it is failing to. The question for receiving directly into your format would be if the transmit and receive word sizes and frame delays have to be the same, or if they can be different - in which case you can probably make the receive side of the SPORT capture the 16 bits you want while ignoring the other 16. – Chris Stratton May 08 '13 at 21:12

0 Answers0