OpenResearchInstitute / pluto_msk

Minimum shift keying top module for PLUTO FPGA
CERN Open Hardware Licence Version 2 - Weakly Reciprocal
5 stars 1 forks source link

AXI reads are working but AXI writes are not working #1

Closed Abraxas3d closed 1 month ago

Abraxas3d commented 1 month ago

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.

-abx

mwishek commented 1 month ago

AXI reads and writes are now both functioning correctly using the _devrdl branch.