PiGraham
Posts: 5292
Joined: Fri Jun 07, 2013 12:37 pm
Location: Waterlooville

Re: Recommended GPIO to read a 10Mhz clock

Fri Feb 03, 2023 1:28 pm

vikkubaliga wrote:
Fri Feb 03, 2023 12:13 pm
jdb wrote:
Fri Feb 03, 2023 11:14 am
Why not use SMI as a bus analyser?

https://github.com/raspberrypi/linux/bl ... _smi_dev.c
The SMI interface is 18bits data. My requirement is 12 bits, 2 ch data. That is 24 bits, plus a clock signal (If same clock can be supplied to both ADC, else one bit per clock)

So, SMI is not possible.

Anyways, except the clock, I'm able to read data at about 40MSPS. Only clock pin is unable to be read.
Not sure if it is hardware or software issue.
How about inverting the clock to one ADC and combining both 12 bit data out onto a data bus via tri-state buffers (or OE on the ADCs)?
If you can capture at 20MHz you would get 24 bits interleaved.
Or use a latch to delay one ADC output by half a clock period.

stevend
Posts: 681
Joined: Fri Oct 11, 2013 12:28 pm

Re: Recommended GPIO to read a 10Mhz clock

Fri Feb 03, 2023 1:50 pm

PhilE wrote:
Thu Feb 02, 2023 2:28 pm
@stevend As far as I know there is no way to use a GPIO as the source of a pacing signal - the "dreqs" are strictly on-chip signals hooked up to peripheral interfaces, which in turn base derive them from FIFO levels, etc.
Phil,
That was the way I also read the data sheet.

But I did wonder whether a timer could trigger the DMA as well as being a source of the 10MHz sampling clock. Might need a bit of creative use of a peripheral to achieve it, but pretty much all peripherals look as if they can pace the DMA (4.2.1.3 in the data sheet). It needs to be one which runs continuously (which IIRC rules out SPI), but PWM or PCM, maybe?

vikkubaliga
Posts: 32
Joined: Wed Oct 19, 2022 9:39 am

Re: Recommended GPIO to read a 10Mhz clock

Fri Feb 03, 2023 2:11 pm

PiGraham wrote:
Fri Feb 03, 2023 1:28 pm

How about inverting the clock to one ADC and combining both 12 bit data out onto a data bus via tri-state buffers (or OE on the ADCs)?
If you can capture at 20MHz you would get 24 bits interleaved.
Or use a latch to delay one ADC output by half a clock period.
This idea is very good. I need to be sure if using this will make it possible for the clock to be read.

This comes at an additional cost to us:
1. Adding the tristate buffers.
2. Adding clock inversion logic.
3. Full layout re-routing
4. Full PCB testing
[If clock reading is a hardware issue and cannot be read even here, I'm left with more problems though. Guessing which data is from which ADC :lol: ]

So, for trial purpose, I can connect my clock signal to one of the SMI data lines and try to read with SMI. I can check just that pin from the read data to confirm.

If I'm able to read either the clock from oscillator or signal from another CM4, then I can go with the above idea.

I can try it out, like I tried DMA. DMA had data rate of 4MSPS, way below my requirement [If my DMA settings were correct] of theoretical min 20MSPS.

PiGraham
Posts: 5292
Joined: Fri Jun 07, 2013 12:37 pm
Location: Waterlooville

Re: Recommended GPIO to read a 10Mhz clock

Fri Feb 03, 2023 3:33 pm

vikkubaliga wrote:
Fri Feb 03, 2023 2:11 pm

[If clock reading is a hardware issue and cannot be read even here, I'm left with more problems though. Guessing which data is from which ADC :lol: ]
That may not be a problem. The SMI has address & data. Possibly you can read each ADC at a different address (e.g. 0 or 1)

Just a thought...

If you have two buffers addressed by just address bit 0 on SMI ADC A & B appear at every even | odd address in SMI memory map..
If you can get SMI to do a DMA block transfer from SMI to host buffer at 20MS/s the address bit 0 will enable the buffers and address bit 1 will be a 10Mz clock that could maybe drive the ADCs and synchronise the conversion and data reading.

I don't know the SMI so I don't know if that is viable.

PiGraham
Posts: 5292
Joined: Fri Jun 07, 2013 12:37 pm
Location: Waterlooville

Re: Recommended GPIO to read a 10Mhz clock

Fri Feb 03, 2023 5:41 pm

This looks useful info on fast acquisition from ADC using SMI & DMA > 10MS/s

https://iosoft.blog/2020/07/16/raspberr ... ace%20here.

vikkubaliga
Posts: 32
Joined: Wed Oct 19, 2022 9:39 am

Re: Recommended GPIO to read a 10Mhz clock

Sat Feb 04, 2023 11:37 am

Below is the plot of my captured data for an input of 1Mhz sine wave.
[This a very good capture, considering only 10 points to plot a sine wave. Very impressed.]

The sine wave of 1Mhz, sampled by ADC at 10Mhz. So, ADC gives 10 points per sine wave.
My code captures data at 40Mhz. So, one ADC sample is read 4 times on an average.
So, finally one sine wave in my captured data will have 10 groups of 4 points each.
1m.png
1m.png (45.98 KiB) Viewed 705 times
Last edited by vikkubaliga on Sat Feb 04, 2023 1:48 pm, edited 1 time in total.

vikkubaliga
Posts: 32
Joined: Wed Oct 19, 2022 9:39 am

Re: Recommended GPIO to read a 10Mhz clock

Sat Feb 04, 2023 1:46 pm

So here is one more update. I had tried this previously though. Mentioning it now.

Instead of generating an external clock, I generated it on the same CM4.
One program to write 1 to the GPSET0 and GPCLR0 in a tight infinite loop.
The other program is the old program that reads from GPLEV0 in a tight loop.

Up till 5-6Mhz generated by the 1st program... the 2nd program is able to capture the levels properly.
As the frequency increase, the levels in captured data become as prior results.

Measuring the pin (GPIO 0) toggled by the 1st program in a CRO shows 10Mhz square wave very similar to one generated by the clock IC.

Not able to read both :cry:

Below is the pic for comparison.
10icm4.jpeg
10icm4.jpeg (207.92 KiB) Viewed 690 times

vikkubaliga
Posts: 32
Joined: Wed Oct 19, 2022 9:39 am

Re: Recommended GPIO to read a 10Mhz clock

Thu Feb 09, 2023 6:32 am

So, is there some leads that I can try or verify?

Like 7Mhz filter effect being generated. Can I measure it?
PhilE wrote:
Wed Feb 01, 2023 8:40 am
Not as far as the SoC is concerned. There should be just about enough bandwidth to resolve a 25MHz clock, so 10MHz should be fine.
Can I find this data in any documentation? I can run anything to confirm this?

PiGraham
Posts: 5292
Joined: Fri Jun 07, 2013 12:37 pm
Location: Waterlooville

Re: Recommended GPIO to read a 10Mhz clock

Thu Feb 09, 2023 10:06 am

This is puzzling.
There are a two main reasons why a digital input might not capture a waveform accurately.

Bandwidth

If high frequencies don't make it to the input pin amplitude and slew rate can fall off so that you don't get unambiguous transitions of the input thresholds. The 'scope traces suggest this is not the problem here.

Sample rate

If sample rate is not > 2x highest signal frequency (Nyquist limit) you will get aliasing and something looking like random data

Descriptions of what happens in the data at >7MHz seems to match this, but the sample rate seems to be well in excess of the Nyquist limit.

I think my next step would be to home in on the point where capture breaks down and gradually increase square wave frequency through the 5MHz to 10MHz range. As far as possible avoid loading the CPU with anything but the data capture. Don't generate the clock on the same CM4, don't output to terminal or file, don't use the network. Just stuff the gpio register value into a buffer as fast as possible for a bit, then stop and analyse the data.

I would expect to see Fsample rate/ F signal rate data points over the input signal cycle until you get to aliasing.

vikkubaliga
Posts: 32
Joined: Wed Oct 19, 2022 9:39 am

Re: Recommended GPIO to read a 10Mhz clock

Thu Feb 09, 2023 10:36 am

PiGraham wrote:
Thu Feb 09, 2023 10:06 am
.... As far as possible avoid loading the CPU with anything but the data capture. Don't generate the clock on the same CM4, don't output to terminal or file, don't use the network. Just stuff the gpio register value into a buffer as fast as possible for a bit, then stop and analyse the data.
Exactly wat I have tried. Generating the clock signal from an external CM4.

No other code in the running program. Simple read, display after everything. Below is the code that I run.

Code: Select all

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

}

And here is the instructions to build it:

1. Execute everything below in root mode.

Code: Select all

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 
Here is the instruction to execute it
2. To increase stack size, freq and set performance mode

Code: Select all

cpufreq-set -g performance
cpufreq-set -u 1500MHz -d 1500MHz
ulimit -s 671088640


3. Run with higher priority

Code: Select all

nice -n -20 ./print_batch

Doing this will read at about 35MSPS. To achieve the max 40MSPS, this code must be run as a service, and triggered by some mechanism to start and stop. I had used shared memory IPC. Anyways, the above code must be enough to test the 10Mhz clock.

PiGraham
Posts: 5292
Joined: Fri Jun 07, 2013 12:37 pm
Location: Waterlooville

Re: Recommended GPIO to read a 10Mhz clock

Thu Feb 09, 2023 11:08 am

I don't think you have posted a plot of data capturing a square wave at, say, 5MHz, 6MHz, 7MHz & 8MHz
The only plot was of ADC values of a 1MZ sine wave here viewtopic.php?t=346594&start=50#p2077865

In the beginning you posted:
vikkubaliga wrote:
Tue Jan 31, 2023 8:12 am
I'm able to capture the data from ADC digital output pins at 40Mhz. But the clock connected to the GPIO14 is not being read. it shows junk data.

Also, I tired generating a square wave from another CM4 and connected it to the same GPIO14 and tried reading it with the same code. Im able to capture the square wave at the pin up to 7Mhz. After 7Mhz, the data is similar to the clock capture.

It may be useful to delve into what "junk data" is. It could be aliasing, despite the numbers seeming to exclude it.
Can you plot a nice square wave at 6.5MHz that turns to "junk" at 7.5MHz?

Is it behaves like aliasing and you can identify an apparent nyquist frequency for it, that could give some clues.

I would try this because the info presented so far seems to be contradictory so assumptions need to be tested.

Another thing I would try is to toggle an output bit on each read. It will slow the sample rate a bit, but it will give you a timing reference you can check on the 'scope. You can confirm if there is indeed a sample point on the high and another on the low of every cycle.

vikkubaliga
Posts: 32
Joined: Wed Oct 19, 2022 9:39 am

Re: Recommended GPIO to read a 10Mhz clock

Thu Feb 09, 2023 11:44 am

PiGraham wrote:
Thu Feb 09, 2023 11:08 am
I don't think you have posted a plot of data capturing a square wave at, say, 5MHz, 6MHz, 7MHz & 8MHz
The only plot was of ADC values of a 1MZ sine wave here viewtopic.php?t=346594&start=50#p2077865
Yes, I have not posted the plot of the clock captures. I thought it wouldn't be necessary because it was junk data. Anyways I'll redo the experiments and post the plots of the clock pins here. So the setup will be such that the clock will be generated by another CM4, and will be connected to one GPIO in the reading CM4.

The 1Mhz sine wave to the ADC with 10Mhz reference clock will still be there to the reading CM4. It serves as the performance measurement criteria to me, by the number of samples read per sine wave of 1Mhz. The 10MHz clock to the ADC will be independent from the clock generated by the other CM4, and this 10Mhz ADC reference clock wont be connected to any GPI0 of the reading CM4.

However, I'm starting to think: Time between the hardware status of the pins reflecting in the GPLEV0 is high enough right? It isn't causing the issue right? Like it might update at 5Mhz to 7Mhz... making not possible to catch a 10Mhz clock at all!!! is there any documentation regarding this timings? Though it has been mentioned in the forum that the bandwidth is about 25Mhz.

Anyways, Ill generate and post the captures here. It will take sometime to setup the hardware again though. Will do it ASAP.

vikkubaliga
Posts: 32
Joined: Wed Oct 19, 2022 9:39 am

Re: Recommended GPIO to read a 10Mhz clock

Mon Feb 27, 2023 6:16 am

So, I've made huge progress. But not enough to get the system working.

I tried ultibo, to do this. And based on the help from the forum, I'm able to read the clock data without any changes in the hardware. They suggested to use DMA and provided some sample code. They're using DMA4 to read the data. Reading seed is about 45M samples per second. Using that, I'm able to read the clock data and perform my filtering as well.
Here is the link to the ultibo forum with this discussion. https://ultibo.org/forum/viewtopic.php? ... 669#p13669

It has read-speed consistency issues. The speed at which data being read is not consistent. Anyways that is another issue.

I'm trying to implement the same in C in raspberry pi OS. But I cannot find any sample source for DMA4 anywhere. I tried modifying the DMA in
https://github.com/fandahao17/Raspberry-Pi-DMA-Tutorial It doesn't work. I get bus error, segmentation error or it reads 0.

Here is the code that I modified to use DMA4 engine, channel 13. Please provide some sample code or tutorials to use DM4, high performance engine in C/C++.

Code: Select all

/*
This is free and unencumbered software released into the public domain.

Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.

In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

For more information, please refer to <http://unlicense.org/>
*/

#include <stdio.h>
#include <assert.h>
#include <unistd.h>
#include <time.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <signal.h>
#include <bcm_host.h>

#include "mailbox.h"

/*
 * Check more about Raspberry Pi's register mapping at:
 * https://www.raspberrypi.org/app/uploads/2012/02/BCM2835-ARM-Peripherals.pdf
 * https://elinux.org/BCM2835_registers
 */
#define PAGE_SIZE 4096

#define GPIO_BASE 0x00200000
#define GPLEV0 0x34
#define GPIO_LEN 0xF4
#define PERI_BUS_BASE 0x7E000000
#define PERI_PHYS_BASE 0x3F000000
#define BUS_TO_PHYS(x) ((x) & ~0xC0000000)

#define CM_BASE 0x00101000
#define CM_LEN 0xA8
#define CM_PWM 0xA0
#define CLK_CTL_BUSY (1 << 7)
#define CLK_CTL_KILL (1 << 5)
#define CLK_CTL_ENAB (1 << 4)
#define CLK_CTL_SRC(x) ((x) << 0)

#define CLK_SRCS 2

#define CLK_CTL_SRC_OSC 1
#define CLK_CTL_SRC_PLLD 6

#define CLK_OSC_FREQ 19200000
#define CLK_OSC_FREQ_2711 54000000
#define CLK_PLLD_FREQ 500000000
#define CLK_PLLD_FREQ_2711 750000000

#define CLK_DIV_DIVI(x) ((x) << 12)

#define BCM_PASSWD (0x5A << 24)

#define PWM_BASE 0x0020C000
#define PWM_LEN 0x28
#define PWM_FIFO 0x18

/* PWM control bits */
#define PWM_CTL 0
#define PWM_STA 1
#define PWM_DMAC 2
#define PWM_RNG1 4
#define PWM_DAT1 5
#define PWM_RNG2 8
#define PWM_DAT2 9

#define PWM_CTL_MSEN2 (1 << 15)
#define PWM_CTL_PWEN2 (1 << 8)
#define PWM_CTL_MSEN1 (1 << 7)
#define PWM_CTL_CLRF1 (1 << 6)
#define PWM_CTL_USEF1 (1 << 5)
#define PWM_CTL_MODE1 (1 << 1)
#define PWM_CTL_PWEN1 (1 << 0)

#define PWM_DMAC_ENAB (1 << 31)
#define PWM_DMAC_PANIC(x) ((x) << 8)
#define PWM_DMAC_DREQ(x) (x)

#define SYST_BASE 0x3000
#define SYST_LEN 0x1C
#define SYST_CLO 0x04

#define DMA_BASE 0x00007000
#define DMA_CHANNEL 13
#define DMA_OFFSET 0x100
#define DMA_ADDR (DMA_BASE + DMA_OFFSET * (DMA_CHANNEL >> 2))

/* DMA CS Control and Status bits */
#define DMA_ENABLE (0xFF0 / 4)
#define DMA_CHANNEL_RESET (1 << 31)
#define DMA_CHANNEL_ABORT (1 << 30)
#define DMA_WAIT_ON_WRITES (1 << 28)
#define DMA_PANIC_PRIORITY(x) ((x) << 20)
#define DMA_PRIORITY(x) ((x) << 16)
#define DMA_INTERRUPT_STATUS (1 << 2)
#define DMA_END_FLAG (1 << 1)
#define DMA_ACTIVE (1 << 0)
#define DMA_DISDEBUG (1 << 28)

/* DMA control block "info" field bits */
#define DMA_NO_WIDE_BURSTS (1 << 26)
#define DMA_PERIPHERAL_MAPPING(x) ((x) << 16)
#define DMA_BURST_LENGTH(x) ((x) << 12)
#define DMA_SRC_IGNORE (1 << 11)
#define DMA_SRC_DREQ (1 << 10)
#define DMA_SRC_WIDTH (1 << 9)
#define DMA_SRC_INC (1 << 8)
#define DMA_DEST_IGNORE (1 << 7)
#define DMA_DEST_DREQ (1 << 6)
#define DMA_DEST_WIDTH (1 << 5)
#define DMA_DEST_INC (1 << 4)
#define DMA_WAIT_RESP (1 << 3)

// https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface
#define MEM_FLAG_DIRECT (1 << 2)
#define MEM_FLAG_COHERENT (2 << 2)
#define MEM_FLAG_L1_NONALLOCATING (MEM_FLAG_DIRECT | MEM_FLAG_COHERENT)

#define TICK_CNT 200
#define CB_CNT (TICK_CNT * 2)

#define CLK_DIVI 5
#define CLK_MICROS 1

typedef struct DMACtrlReg
{
    uint32_t cs;      // DMA Channel Control and Status register
    uint32_t cb_addr; // DMA Channel Control Block Address
} DMACtrlReg;

typedef struct DMAControlBlock_old
{
    uint32_t tx_info;    // Transfer information
    uint32_t src;        // Source (bus) address
    uint32_t dest;       // Destination (bus) address
    uint32_t tx_len;     // Transfer length (in bytes)
    uint32_t stride;     // 2D stride
    uint32_t next_cb;    // Next DMAControlBlock (bus) address
    uint32_t padding[2]; // 2-word padding
} DMAControlBlock_old;   

typedef struct DMAControlBlock
{
    uint32_t debug;      // Debug
    uint32_t tx_info;    // Transfer information
    uint32_t src;        // Source (bus) address
    uint32_t src_info;   // Source info
    uint32_t dest;       // Destination (bus) address
    uint32_t dest_info;  // Dest info
    uint32_t tx_len;     // Transfer length (in bytes)
    uint32_t next_cb;    // Next DMAControlBlock (bus) address
    uint32_t debug2;     // Debug 2
    uint32_t padding;    // padding 
} DMAControlBlock;       // DMA4 engine, high performance 40 bits

typedef struct DMAMemHandle
{
    void *virtual_addr; // Virutal base address of the page
    uint32_t bus_addr;  // Bus adress of the page, this is not a pointer because it does not point to valid virtual address
    uint32_t mb_handle; // Used by mailbox property interface
    uint32_t size;
} DMAMemHandle;

typedef struct CLKCtrlReg
{
    // See https://elinux.org/BCM2835_registers#CM
    uint32_t ctrl;
    uint32_t div;
} CLKCtrlReg;

int mailbox_fd = -1;
DMAMemHandle *dma_cbs;
DMAMemHandle *dma_ticks;
volatile DMACtrlReg *dma_reg;
volatile PWMCtrlReg *pwm_reg;
volatile CLKCtrlReg *clk_reg;

DMAMemHandle *dma_malloc(unsigned int size)
{
    if (mailbox_fd < 0)
    {
        mailbox_fd = mbox_open();
        assert(mailbox_fd >= 0);
    }

    // Make `size` a multiple of PAGE_SIZE
    size = ((size + PAGE_SIZE - 1) / PAGE_SIZE) * PAGE_SIZE;

    DMAMemHandle *mem = (DMAMemHandle *)malloc(sizeof(DMAMemHandle));
    // Documentation: https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface
    mem->mb_handle = mem_alloc(mailbox_fd, size, PAGE_SIZE, MEM_FLAG_L1_NONALLOCATING);
    mem->bus_addr = mem_lock(mailbox_fd, mem->mb_handle);
    mem->virtual_addr = mapmem(BUS_TO_PHYS(mem->bus_addr), size);
    mem->size = size;

    assert(mem->bus_addr != 0);

    fprintf(stderr, "MBox alloc: %d bytes, bus: %08X, virt: %08X\n", mem->size, mem->bus_addr, (uint32_t)mem->virtual_addr);

    return mem;
}

void dma_free(DMAMemHandle *mem)
{
    if (mem->virtual_addr == NULL)
        return;

    unmapmem(mem->virtual_addr, mem->size);
    mem_unlock(mailbox_fd, mem->mb_handle);
    mem_free(mailbox_fd, mem->mb_handle);
    mem->virtual_addr = NULL;
}

void *map_peripheral(uint32_t addr, uint32_t size)
{
    int mem_fd;
    // Check mem(4) about /dev/mem
    if ((mem_fd = open("/dev/mem", O_RDWR | O_SYNC)) < 0)
    {
        perror("Failed to open /dev/mem: ");
        exit(-1);
    }

    uint32_t *result = (uint32_t *)mmap(
        NULL,
        size,
        PROT_READ | PROT_WRITE,
        MAP_SHARED,
        mem_fd,
        bcm_host_get_peripheral_address() + addr);

    close(mem_fd);

    if (result == MAP_FAILED)
    {
        perror("mmap error: ");
        exit(-1);
    }
    return result;
}

void dma_alloc_buffers()
{
    dma_cbs = dma_malloc(CB_CNT * sizeof(DMAControlBlock));
    dma_ticks = dma_malloc(TICK_CNT * sizeof(uint32_t));
}

static inline DMAControlBlock *ith_cb_virt_addr(int i) { return (DMAControlBlock *)dma_cbs->virtual_addr + i; }

static inline uint32_t ith_cb_bus_addr(int i) { return dma_cbs->bus_addr + i * sizeof(DMAControlBlock); }

static inline uint32_t *ith_tick_virt_addr(int i) { return (uint32_t *)dma_ticks->virtual_addr + i; }

static inline uint32_t ith_tick_bus_addr(int i) { return dma_ticks->bus_addr + i * sizeof(uint32_t); }

void dma_init_cbs()
{
    DMAControlBlock *cb;
   // for (int i = 0; i < TICK_CNT; i++)
    {
        // Tick block
        cb = ith_cb_virt_addr(0);
        cb->debug = 0x1D0CC400;
        cb->tx_info = DMA_DEST_INC;
        //cb->src = PERI_BUS_BASE + SYST_BASE + SYST_CLO;
        cb->src = PERI_BUS_BASE + GPIO_BASE + GPLEV0;
        cb->src_info = 0x00;
        cb->dest = ith_tick_bus_addr(0);
        cb->dest_info = (uint32_t)(1 << 12);
        cb->tx_len = TICK_CNT*4;
        cb->debug2 = 0x00;
        cb->next_cb = 0;
    }
    fprintf(stderr, "Init: %d cbs, %d ticks\n", CB_CNT, TICK_CNT);
}

void dma_start()
{
    // Reset the DMA channel
    dma_reg->cs = DMA_CHANNEL_ABORT;
    dma_reg->cs = 0;
    dma_reg->cs = DMA_CHANNEL_RESET;
    dma_reg->cb_addr = 0;

    dma_reg->cs = DMA_INTERRUPT_STATUS | DMA_END_FLAG;

    // Make cb_addr point to the first DMA control block and enable DMA transfer
    dma_reg->cb_addr = ith_cb_bus_addr(0);
    dma_reg->cs =  (1 << 29); //Disable debug
    dma_reg->cs |= DMA_WAIT_ON_WRITES | DMA_ACTIVE; // yes this is the changes we did when u were in office. 
}

void dma_end()
{
    // Shutdown DMA channel, otherwise it won't stop after program exits
    dma_reg->cs |= DMA_CHANNEL_ABORT;
    usleep(100);
    dma_reg->cs &= ~DMA_ACTIVE;
    dma_reg->cs |= DMA_CHANNEL_RESET;
    usleep(100);

    // Release the memory used by DMA, otherwise the memory will be leaked after program exits
    dma_free(dma_ticks);
    dma_free(dma_cbs);

    free(dma_ticks);
    free(dma_cbs);
}

int main ()
{
    uint8_t *dma_base_ptr = map_peripheral(DMA_BASE, PAGE_SIZE);
    dma_reg = (DMACtrlReg *)(dma_base_ptr + DMA_CHANNEL * 0x100);

    dma_alloc_buffers();
    usleep(100);

    dma_init_cbs();
    usleep(100);

    dma_start();
    usleep(100);

    uint32_t ticks[TICK_CNT];
    memcpy(ticks, ith_tick_virt_addr(0), TICK_CNT * sizeof(uint32_t));

    for (size_t i = 0; i < TICK_CNT; i++)
    {
        printf("DMA %d: %u\n", i, ticks[i]);
    }

    dma_end();
    return 0;
}

vikkubaliga
Posts: 32
Joined: Wed Oct 19, 2022 9:39 am

Re: Recommended GPIO to read a 10Mhz clock

Mon Feb 27, 2023 9:25 am

PiGraham wrote:
Thu Feb 09, 2023 11:08 am
It may be useful to delve into what "junk data" is. It could be aliasing, despite the numbers seeming to exclude it.
Can you plot a nice square wave at 6.5MHz that turns to "junk" at 7.5MHz?

Is it behaves like aliasing and you can identify an apparent nyquist frequency for it, that could give some clues.
The same hardware, using DMA4 I'm able to read the clock in ultibo. So, now I'm sure that it's a problem with the software. Not necessarily my application C/C++ code, but some internal code.

Because the DMA4 engine works the same way irrespective of the software being used.

I found no examples or implementations of DMA4 on the internet. Can u pls provide some examples or references?

Return to “Compute Module”