I have a 12-bit parallel output ADC, which samples at 10M samples/sec. A 10MHz oscillator clock is connected to it in the custom designed PCB. The 12 data lines are directly connected to the CM4. The ADC continuously samples the data and keeps setting the digital outputs at 10M times a second. The ADC does not have a "Data Ready" pin. So, I need to use the ADC reference clock itself as the data ready as recommended in the ADC datasheet (Read the data lines at the rising edge of the clock).
I'm able to read the ADC samples without any issue by continuously reading GPLEV0 to an array in a tight for loop. The reading speed is 40M samples per second. There is no any issue in this.
I have connected the 10Mhz clock IC to a GPIO (GPIO 0) of my CM4. This was missed in the layout, and we have now connected it by soldering a wire from clock terminal to the GPIO.
I am not able to read this 10MHz clock signal, even though my data capture speed is 40MSPS.
To verify, I generated a square wave in another CM4 and connected it to the same pin in my reading CM4 and tried to read it. Even this is not being captured.
If the external CM4 generated square wave of frequencies less than 7MHz, my program captures it. Extracting only the GPIO 0 data from captured samples produces a good square wave.
As frequencies increases above 7MHz, the data becomes distorted and is nowhere close to square wave.
I also tried to toggle the same GPIO in a tight infinite loop by setting the GPSET0 and GPCLR0 in another program in the same CM4. If some delay is added in between and this freq is about 5MHz, my capturing program is able to catch the square wave. As frequency increase above 5MHz, even this is not being read.
My doubts are:
1. Are all GPIOs capable of reading 10MHz input? or are there any particular GPIOs for high speed?
2. Is it possible to read a 10MHz signal from a GPIO?
Below is my data capturing code:
volatile uint32_t *base = mmap_bcm_register(GPIO_REGISTER_BASE);
// ...
uint32_t adc_data[BATCH+10];// = (uint32_t *) malloc(copy_size);
for(uint32_t idx=0;idx<BATCH;){ adc_data[++idx] = base[13];};
//...
Also below is the image of 1Mhz sine wave captured by the above code: 1Mhz samples with 10MHz = 10 points per sine wave. 10Mhz samples with 40MSPS = 4 points per samples. Thus 1 sine wave = 10 groups of 4-5 same value points.
I have tried DMA from the GPLEV0 to mailbox. Reading speed is much lower. About 4MSPS.
I have tried varying the capturing speed by adding delay in the for loop. No results.
I have tried to compare and check the clock levels in the loop. No results.
// 1. Read only when clock is high - Not able to read clock
{ uint32_t raw = base[13]; arr[idx] = raw; idx = idx + (raw&1); }
// 2. Read at an edge by comparing with prev ddata - Not able to read clock
{ uint32_t raw = base[13]; arr[idx] = raw; idx = idx + ((prev_data ^ raw)&1); prev_data = raw; }
ADC : LTC1420
Clock : ECS-3225MVLC-100-CN-TR
Edit 1:
Here is the full code, if you want to try it out:
#include <iostream>
#include <cstdlib>
#include <string>
#include <cstring>
#include <cctype>
#include <chrono>
#include <sys/time.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <ctype.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
#include <inttypes.h>
using namespace std;
#define BATCH 4000000
#define REQ_SAMPLES 10000000
#define BTA(arr) { arr[++idx] = base[13]; }
#define BTA_10(arr) { BTA(arr); BTA(arr); BTA(arr); BTA(arr); BTA(arr); BTA(arr); BTA(arr); BTA(arr); BTA(arr); BTA(arr); }
#define BTA_100(arr) { BTA_10(arr); BTA_10(arr); BTA_10(arr); BTA_10(arr); BTA_10(arr); BTA_10(arr); BTA_10(arr); BTA_10(arr); BTA_10(arr); BTA_10(arr); }
#define BTA_500(arr) { BTA_10(arr); BTA_10(arr); BTA_10(arr); BTA_10(arr); BTA_10(arr); }
#define BTA_1k(arr) { BTA_100(arr); BTA_100(arr); BTA_100(arr); BTA_100(arr); BTA_100(arr); BTA_100(arr); BTA_100(arr); BTA_100(arr); BTA_100(arr); BTA_100(arr); }
#define BASE_TO_ARRAY(arr) for(uint32_t idx=0;idx<BATCH;){ BTA_1k(arr); };
const uint32_t copy_size = BATCH * 4;
const uint32_t no_of_batches = REQ_SAMPLES / BATCH;
#define BCM2711_PI4_PERI_BASE 0xFE000000
#define PERI_BASE BCM2711_PI4_PERI_BASE
#define PAGE_SIZE 4096
#define GPIO_REGISTER_BASE 0x200000
// Return a pointer to a periphery subsystem register.
static uint32_t *mmap_bcm_register(off_t register_offset) {
const off_t base = PERI_BASE;
int mem_fd;
if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) {
perror("can't open /dev/mem: ");
fprintf(stderr, "You need to run this as root!\n");
return NULL;
}
uint32_t *result =
(uint32_t*) mmap(NULL, // Any adddress in our space will do
PAGE_SIZE,
PROT_READ|PROT_WRITE, // Enable r/w on GPIO registers.
MAP_SHARED,
mem_fd, // File to map
base + register_offset // Offset to bcm register
);
close(mem_fd);
if (result == MAP_FAILED) {
fprintf(stderr, "mmap error %p\n", result);
return NULL;
}
return result;
}
int main()
{
uint32_t adc_data[BATCH+10];// = (uint32_t *) malloc(copy_size);
volatile uint32_t *base = mmap_bcm_register(GPIO_REGISTER_BASE) ;
BASE_TO_ARRAY(adc_data);
BASE_TO_ARRAY(adc_data);
BASE_TO_ARRAY(adc_data);
for(int idx =4000; idx < 14000; idx++){
uint32_t raw = adc_data[idx];
printf("%d %d\r\n",idx,raw&1);
}
return 0;
}
[Execute everything from here on in root mode itself]
- Build the code using:
g++ -o print_batch print_batch.cpp -O3 -W -Wall -D_XOPEN_SOURCE=500 -funroll-all-loops -ftree-vectorize -static -mcpu=cortex-a72
strip print_batch
- Set the frequency, stack size etc:
cpufreq-set -g performance
cpufreq-set -u 1500MHz -d 1500MHz
ulimit -s 671088640
- Execute with higher priority:
nice -n -20 ./print_batch
This will read the GPLEV0 at about 35MSPS only. To read this at 40MSPS, the print_batch must be run in a service. It must be modified to have a trigger to start and stop the acquisition. (That is how the image above was generated. Run in a service, trigger to start and stop. Not posted here because it increases the setup complexity if anyone needs to try it out.)
Anyways, for the 10MHz clock, 35MHz must be more than enough.
Edit 2:
Here is a link to the raspberry pi forum with the same questions I have asked. https://forums.raspberrypi.com/viewtopic.php?t=346594