Using current MSK bitfile, xsa, and PLUTO firmware build, AXI reads from MSK are working, but writes are not working.
Reads and writes to TX_DMAC scratch register are working.
Here's an example output followed by the code listing.
Hello World! Running TX-DMA access tests.
Opening a character device file in DDR memory.
Memory map the address of the TX-DMAC via its AXI lite control interface register block.
Memory map the address of the MSK block via its AXI lite control interface.
Create a buffer for some transmitted data.
TX DMAC Interface Description (0x00070313@0x0010):
Writing to scratch register in TX-DMAC.
Reading from scratch register in TX-DMAC. We see: (0x5555aaaa@0008)
Reading the TX-DMAC peripheral ID: (0x00000000@0004)
Reading from MSK block HASH ID: (0xaaaa5555@0000)
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
Configure MSK for minimum viable product test.
Initialize MSK block.
Read MSK_INIT: (0x00000000@0004)
* Destroying buffers
packet_write_wait: Connection to 192.168.3.1 port 22: Broken pipe
Code that created the above output:
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* msk_test IIO streaming test code
*
* Copyright (C) 2024 IABG mbH and ORI
* Author: Michael Feilen <[feilen_at_iabg.de](http://feilen_at_iabg.de/)> and Abraxas3d
**/#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <signal.h>
#include <stdio.h>
#include <iio.h>/* for memory management */
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <sys/mman.h>/* helper macros */
#define MHZ(x) ((long long)(x*1000000.0 + .5))
#define GHZ(x) ((long long)(x*1000000000.0 + .5))#define IIO_ENSURE(expr) { \
if (!(expr)) { \
(void) fprintf(stderr, "assertion failed (%s:%d)\n", __FILE__, __LINE__); \
(void) abort(); \
} \
}#define TX_DMAC_CONTROL_REGISTER 0x00
#define TX_DMAC_STATUS_REGISTER 0x04
#define TX_DMAC_IDENTIFICATION 0x000c
#define TX_DMAC_SCRATCH 0x0008
#define TX_DMAC_INTERFACE 0x0010
#define TX_DMAC_DEST_ADDRESS 0x0410
#define TX_DMAC_SRC_ADDRESS 0x0414
#define TX_DMAC_PERIPHERAL_ID 0x004
#define ENCODER_CONTROL_REGISTER 0x00
#define MSK_HASH_ID 0x00
#define MSK_INIT 0x0004
#define MSK_PTT 0x0008
#define MSK_RX_CONFIG 0x000c
#define MSK_FREQ_WORD_FT 0x0010
#define MSK_FREQ_WORD_F1 0x0014
#define MSK_FREQ_WORD_F2 0x0018unsigned int read_dma(unsigned int *virtual_addr, int offset)
{
return virtual_addr[offset>>2];
}unsigned int write_dma(unsigned int *virtual_addr, int offset, unsigned int value)
{ virtual_addr[offset>>2] = value;
return 0;
}void dma_interface(unsigned int *virtual_addr)
{
unsigned int interface = read_dma(virtual_addr, TX_DMAC_INTERFACE);
printf("TX DMAC Interface Description (0x%08x@0x%04x):\n", interface, TX_DMAC_INTERFACE);
//break out and parse the fields in human readable format
}/* RX is input, TX is output */
enum iodev { RX, TX };/* common RX and TX streaming params */
struct stream_cfg {
long long bw_hz; // Analog banwidth in Hz
long long fs_hz; // Baseband sample rate in Hz
long long lo_hz; // Local oscillator frequency in Hz
const char* rfport; // Port name
};/* static scratch mem for strings */
static char tmpstr[64];/* IIO structs required for streaming */
static struct iio_context *ctx = NULL;
static struct iio_channel *rx0_i = NULL;
static struct iio_channel *rx0_q = NULL;
static struct iio_channel *tx0_i = NULL;
static struct iio_channel *tx0_q = NULL;
static struct iio_buffer *rxbuf = NULL;
static struct iio_buffer *txbuf = NULL;static bool stop;/* cleanup and exit */
static void shutdown(void)
{
printf("* Destroying buffers\n");
if (rxbuf) { iio_buffer_destroy(rxbuf); }
if (txbuf) { iio_buffer_destroy(txbuf); } printf("* Disabling streaming channels\n");
if (rx0_i) { iio_channel_disable(rx0_i); }
if (rx0_q) { iio_channel_disable(rx0_q); }
if (tx0_i) { iio_channel_disable(tx0_i); }
if (tx0_q) { iio_channel_disable(tx0_q); } printf("* Destroying context\n");
if (ctx) { iio_context_destroy(ctx); }
exit(0);
}static void handle_sig(int sig)
{
printf("Waiting for process to finish... Got signal %d\n", sig);
stop = true;
}/* check return value of attr_write function */
static void errchk(int v, const char* what) {
if (v < 0) { fprintf(stderr, "Error %d writing to channel \"%s\"\nvalue may not be supported.\n", v, what); shutdown(); }
}/* write attribute: long long int */
static void wr_ch_lli(struct iio_channel *chn, const char* what, long long val)
{
errchk(iio_channel_attr_write_longlong(chn, what, val), what);
}/* write attribute: string */
static void wr_ch_str(struct iio_channel *chn, const char* what, const char* str)
{
errchk(iio_channel_attr_write(chn, what, str), what);
}/* helper function generating channel names */
static char* get_ch_name(const char* type, int id)
{
snprintf(tmpstr, sizeof(tmpstr), "%s%d", type, id);
return tmpstr;
}/* returns ad9361 phy device */
static struct iio_device* get_ad9361_phy(void)
{
struct iio_device *dev = iio_context_find_device(ctx, "ad9361-phy");
IIO_ENSURE(dev && "No ad9361-phy found");
return dev;
}/* finds AD9361 streaming IIO devices */
static bool get_ad9361_stream_dev(enum iodev d, struct iio_device **dev)
{
switch (d) {
case TX: *dev = iio_context_find_device(ctx, "cf-ad9361-dds-core-lpc"); return *dev != NULL;
case RX: *dev = iio_context_find_device(ctx, "cf-ad9361-lpc"); return *dev != NULL;
default: IIO_ENSURE(0); return false;
}
}/* finds AD9361 streaming IIO channels */
static bool get_ad9361_stream_ch(enum iodev d, struct iio_device *dev, int chid, struct iio_channel **chn)
{
*chn = iio_device_find_channel(dev, get_ch_name("voltage", chid), d == TX);
if (!*chn)
*chn = iio_device_find_channel(dev, get_ch_name("altvoltage", chid), d == TX);
return *chn != NULL;
}/* finds AD9361 phy IIO configuration channel with id chid */
static bool get_phy_chan(enum iodev d, int chid, struct iio_channel **chn)
{
switch (d) {
case RX: *chn = iio_device_find_channel(get_ad9361_phy(), get_ch_name("voltage", chid), false); return *chn != NULL;
case TX: *chn = iio_device_find_channel(get_ad9361_phy(), get_ch_name("voltage", chid), true); return *chn != NULL;
default: IIO_ENSURE(0); return false;
}
}/* finds AD9361 local oscillator IIO configuration channels */
static bool get_lo_chan(enum iodev d, struct iio_channel **chn)
{
switch (d) {
// LO chan is always output, i.e. true
case RX: *chn = iio_device_find_channel(get_ad9361_phy(), get_ch_name("altvoltage", 0), true); return *chn != NULL;
case TX: *chn = iio_device_find_channel(get_ad9361_phy(), get_ch_name("altvoltage", 1), true); return *chn != NULL;
default: IIO_ENSURE(0); return false;
}
}/* applies streaming configuration through IIO */
bool cfg_ad9361_streaming_ch(struct stream_cfg *cfg, enum iodev type, int chid)
{
struct iio_channel *chn = NULL; // Configure phy and lo channels
printf("* Acquiring AD9361 phy channel %d\n", chid);
if (!get_phy_chan(type, chid, &chn)) { return false; }
wr_ch_str(chn, "rf_port_select", cfg->rfport);
wr_ch_lli(chn, "rf_bandwidth", cfg->bw_hz);
wr_ch_lli(chn, "sampling_frequency", cfg->fs_hz); // Configure LO channel
printf("* Acquiring AD9361 %s lo channel\n", type == TX ? "TX" : "RX");
if (!get_lo_chan(type, &chn)) { return false; }
wr_ch_lli(chn, "frequency", cfg->lo_hz);
return true;
}/* simple configuration and streaming */
/* usage:
* Default context, assuming local IIO devices, i.e., this script is run on ADALM-Pluto for example
$./a.out
* URI context, find out the uri by typing `iio_info -s` at the command line of the host PC
$./a.out usb:x.x.x
*/
int main (int argc, char **argv)
{
// Streaming devices
struct iio_device *tx;
struct iio_device *rx; // RX and TX sample counters
size_t nrx = 0;
size_t ntx = 0; // Stream configurations
struct stream_cfg rxcfg;
struct stream_cfg txcfg; // Listen to ctrl+c and IIO_ENSURE
signal(SIGINT, handle_sig);//!!!
// RX stream config
[rxcfg.bw](http://rxcfg.bw/)_hz = MHZ(2); // 2 MHz rf bandwidth
rxcfg.fs_hz = MHZ(2.5); // 2.5 MS/s rx sample rate
rxcfg.lo_hz = GHZ(2.5); // 2.5 GHz rf frequency
rxcfg.rfport = "A_BALANCED"; // port A (select for rf freq.) // TX stream config
[txcfg.bw](http://txcfg.bw/)_hz = MHZ(1.5); // 1.5 MHz rf bandwidth
txcfg.fs_hz = MHZ(2.5); // 2.5 MS/s tx sample rate
txcfg.lo_hz = GHZ(2.5); // 2.5 GHz rf frequency
txcfg.rfport = "A"; // port A (select for rf freq.) printf("* Acquiring IIO context\n");
if (argc == 1) {
IIO_ENSURE((ctx = iio_create_default_context()) && "No context");
}
else if (argc == 2) {
IIO_ENSURE((ctx = iio_create_context_from_uri(argv[1])) && "No context");
}
IIO_ENSURE(iio_context_get_devices_count(ctx) > 0 && "No devices"); printf("* Acquiring AD9361 streaming devices\n");
IIO_ENSURE(get_ad9361_stream_dev(TX, &tx) && "No tx dev found");
IIO_ENSURE(get_ad9361_stream_dev(RX, &rx) && "No rx dev found"); printf("* Configuring AD9361 for streaming\n");
IIO_ENSURE(cfg_ad9361_streaming_ch(&rxcfg, RX, 0) && "RX port 0 not found");
IIO_ENSURE(cfg_ad9361_streaming_ch(&txcfg, TX, 0) && "TX port 0 not found"); printf("* Initializing AD9361 IIO streaming channels\n");
IIO_ENSURE(get_ad9361_stream_ch(RX, rx, 0, &rx0_i) && "RX chan i not found");
IIO_ENSURE(get_ad9361_stream_ch(RX, rx, 1, &rx0_q) && "RX chan q not found");
IIO_ENSURE(get_ad9361_stream_ch(TX, tx, 0, &tx0_i) && "TX chan i not found");
IIO_ENSURE(get_ad9361_stream_ch(TX, tx, 1, &tx0_q) && "TX chan q not found"); printf("* Enabling IIO streaming channels\n");
iio_channel_enable(rx0_i);
iio_channel_enable(rx0_q);
iio_channel_enable(tx0_i);
iio_channel_enable(tx0_q); printf("* Creating non-cyclic IIO buffers with 1 MiS\n");
rxbuf = iio_device_create_buffer(rx, 1024*1024, false);
if (!rxbuf) {
perror("Could not create RX buffer");
shutdown();
}
txbuf = iio_device_create_buffer(tx, 1024*1024, false);
if (!txbuf) {
perror("Could not create TX buffer");
shutdown();
} printf("Hello World! Running TX-DMA access tests.\n");
printf("Opening a character device file in DDR memory.\n");
int ddr_memory = open("/dev/mem", O_RDWR | O_SYNC);
printf("Memory map the address of the TX-DMAC via its AXI lite control interface register block.\n");
unsigned int *dma_virtual_addr = mmap(NULL, 65535, PROT_READ | PROT_WRITE, MAP_SHARED, ddr_memory, 0x7c420000); printf("Memory map the address of the MSK block via its AXI lite control interface.\n");
unsigned int *msk_virtual_addr = mmap(NULL, 65535, PROT_READ | PROT_WRITE, MAP_SHARED, ddr_memory, 0x43c00000); printf("Create a buffer for some transmitted data.\n");
unsigned int transmit_data[4*100];
dma_interface(dma_virtual_addr); printf("Writing to scratch register in TX-DMAC.\n");
write_dma(dma_virtual_addr, TX_DMAC_SCRATCH, 0x5555AAAA);
printf("Reading from scratch register in TX-DMAC. We see: (0x%08x@%04x)\n", read_dma(dma_virtual_addr, TX_DMAC_SCRATCH), TX_DMAC_SCRATCH);
printf("Reading the TX-DMAC peripheral ID: (0x%08x@%04x)\n", read_dma(dma_virtual_addr, TX_DMAC_PERIPHERAL_ID), TX_DMAC_PERIPHERAL_ID);
printf("Reading from MSK block HASH ID: (0x%08x@%04x)\n", read_dma(msk_virtual_addr, MSK_HASH_ID), MSK_HASH_ID);
printf("-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
printf("Configure MSK for minimum viable product test.\n");
printf("Initialize MSK block.\n");
printf("Read MSK_INIT: (0x%08x@%04x)\n", read_dma(msk_virtual_addr, MSK_INIT), MSK_INIT);
write_dma(msk_virtual_addr, MSK_INIT, 0x00000000);
// printf("Writing ft, f1, f2.\n");
// write_dma(msk_virtual_addr, MSK_FREQ_WORD_FT, 0x0039d037);
// write_dma(msk_virtual_addr, MSK_FREQ_WORD_F1, 0x044a740e);
// write_dma(msk_virtual_addr, MSK_FREQ_WORD_F2, 0x04be147b);
// printf("Writing loopback enable and rx invert, bit 0 and 31 respectively, of MSK_RX_CONFIG.\n");
// write_dma(msk_virtual_addr, MSK_RX_CONFIG, 0x80000000);
//printf("Reading back MSK_RX_CONFIG status register. We see: (0x%08x@%04x)\n", read_dma(dma_virtual_addr, MSK_RX_CONFIG), MSK_RX_CONFIG); shutdown(); return 0;
}
busybox devmem also fails to write to registers on the target.
Using current MSK bitfile, xsa, and PLUTO firmware build, AXI reads from MSK are working, but writes are not working.
Reads and writes to TX_DMAC scratch register are working.
Here's an example output followed by the code listing.
Code that created the above output:
busybox devmem also fails to write to registers on the target.
-abx