I am trying to use this DMA driver to transfer memory from a streaming device to SDRAM.
This is my design in Qsys/Platform designer:
The design streams a predictable pattern of 6x6x6x6x to SDRAM.
The design is verified as working correctly via u-boot by performing the following in uboot:
SOCFPGA_AGILEX5 # md 0x20010000 1 # Read SysID
20010000: acd5cafe ....
SOCFPGA_AGILEX5 # md 0x00000000 1 # Read SDRAM
00000000: 9400000a ....
SOCFPGA_AGILEX5 # mw 0x22000044 0x1 # Stop Descriptors
SOCFPGA_AGILEX5 # mw 0x22000044 0x20 # Stop Descriptors
SOCFPGA_AGILEX5 # mw 0x22000044 0x2 # Reset Dispatcher
SOCFPGA_AGILEX5 # mw 0x22000064 0x00000000 # Choose destination address
SOCFPGA_AGILEX5 # mw 0x22000068 0x8 # Write length in bytes
SOCFPGA_AGILEX5 # md 0x22000040 1 # Check status
22000040: 00000002 ....
SOCFPGA_AGILEX5 # mw 0x2200006c 0x00004000
SOCFPGA_AGILEX5 # sleep 1
SOCFPGA_AGILEX5 # mw 0x2200006c 0x80004000 # Execute read
SOCFPGA_AGILEX5 # md 0x22000040 1 # Check status
22000040: 00000002 ....
SOCFPGA_AGILEX5 # md 0x00000000 1
00000000: 00000000 ....
SOCFPGA_AGILEX5 # mw 0x22000044 0x2 # Reset Dispatcher
SOCFPGA_AGILEX5 # mw 0x2200006c 0x80004000
SOCFPGA_AGILEX5 # md 0x00000000 1
00000000: 64646464 ....
SOCFPGA_AGILEX5 # mw 0x22000044 0x2
SOCFPGA_AGILEX5 # mw 0x2200006c 0x80004000
SOCFPGA_AGILEX5 # md 0x00000000 1
00000000: 65656565 ....
SOCFPGA_AGILEX5 # mw 0x22000044 0x2
SOCFPGA_AGILEX5 # mw 0x2200006c 0x80004000
SOCFPGA_AGILEX5 # md 0x00000000 1
00000000: 67676767 ....
SOCFPGA_AGILEX5 # mw 0x22000044 0x2
SOCFPGA_AGILEX5 # mw 0x2200006c 0x80004000
SOCFPGA_AGILEX5 # md 0x00000000 1
00000000: 67676767 ....
Based on the README.md in the driver repo, I have changed my device tree to include these elements:
clk_0: clk_0 {
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <100000000>; /* 100.00 MHz */
clock-output-names = "clk_0";
};
msgdma_streaming_read_0: msgdma@22000040 {
compatible = "altr,altera-msgdma-st-1.0";
reg = <0x0 0x22000040 0x0 0x00000020>,
<0x0 0x22000060 0x0 0x00000010>;
reg-names = "csr", "descriptor_slave";
string-property = "msgdma_read";
interrupts = <0 19 4>;
interrupt-parent = <&intc>;
clocks = <&clk_0>;
};
The interrupt number is based on my .qsys design, which included this snippet:
...
<altera:connection altera:kind="interrupt" altera:version="24.3" altera:start="subsys_hps.f2h_irq0_in" altera:end="msgdma_0.csr_irq">
<altera:connection_parameter altera:parameter_name="irqNumber" altera:parameter_value="2">
</altera:connection_parameter>
</altera:connection>
...
The kernel module builds and loads to the SoC:
root@localhost:~# insmod altera_msgdma_st.ko
root@localhost:~# lsmod
Module Size Used by
altera_msgdma_st 20480 0
root@localhost:~# ls /dev | grep msgdma
altera_msgdma_rd0
root@localhost:~# dmesg | tail -1
[ 689.957318] Registered Altera MSGDMA device 0 (in READ mode) ...
I have then written a C program to move data:
dma_read_st.c:
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <string.h>
#include <errno.h>
// https://github.com/pavelfpl/altera_msgdma_st/blob/master/altera_msgdma_ioctl.h
#include "../dma_driver/altera_msgdma_ioctl.h"
#define READ_COUNT 3 // Number of times to read
#define DELAY_US 100000 // Delay between reads in microseconds (100ms)
#define BUFFER_SIZE 8 // Size of the buffer to read
#define MIN_BLOCK_SIZE 8 // Minimum block size for reads
int main() {
const char *device = "/dev/altera_msgdma_rd0";
int fd = open(device, O_RDONLY);
if (fd < 0) {
perror("Failed to open device");
return 1;
}
if (ioctl(fd, IO_ALTERA_MSGDMA_STOP_RESET) < 0) {
perror("Failed to send STOP_RESET ioctl");
close(fd);
return 1;
}
printf("STOP_RESET ioctl sent successfully\n");
unsigned long buffer_size = BUFFER_SIZE;
if (ioctl(fd, IO_ALTERA_MSGDMA_SET_BUFFER_SIZE, &buffer_size) < 0) {
perror("Failed to set buffer size");
close(fd);
return 1;
}
unsigned long block_size = MIN_BLOCK_SIZE;
if (ioctl(fd, IO_ALTERA_MSGDMA_SET_MIN_BLOCK_SIZE, &block_size) < 0) {
perror("Failed to set min block size");
close(fd);
return 1;
}
unsigned char buffer[BUFFER_SIZE];
for (int read_num = 0; read_num < READ_COUNT; read_num++) {
ssize_t bytes_read = read(fd, buffer, sizeof(buffer));
if (bytes_read < 0) {
perror("Failed to read from device");
close(fd);
return 1;
}
printf("Read #%d: %zd bytes:\n", read_num + 1, bytes_read);
for (int i = 0; i < bytes_read; i++) {
printf("0x%02x ", buffer[i]);
}
printf("\n");
usleep(DELAY_US);
memset(buffer, 0, sizeof(buffer));
}
close(fd);
return 0;
}
However, the program outputs the following:
root@localhost:~# ./read_dma_st
STOP_RESET ioctl sent successfully
Read #1: 8 bytes:
0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff
Read #2: 8 bytes:
0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff
Read #3: 8 bytes:
0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff
Instead of the expected 6x6x6x6x pattern.
I am not sure what to try next - does anyone have some experience with this?
Many thanks, K
