Welcome to Hackster!
Hackster is a community dedicated to learning hardware, from beginner to pro. Join us, it's free!
Whitney Knitter
Published © GPL3+

Stream DAC Codes to AWG Zmod from Memory with AXI DMA

This project outlines how to stream DAC codes from DDR memory to the DAC on Digilent's AWG Zmod using MM2S with AXI DMA.

IntermediateFull instructions provided2 hours2,248
Stream DAC Codes to AWG Zmod from Memory with AXI DMA

Things used in this project

Hardware components

Digilent Eclypse Z7: Zynq-7000 SoC Development Board with SYZYGY-compatible Expansion
×1
Digilent Zmod AWG 1411: 2-channel 14-bit Arbitrary Waveform Generator (AWG) Module
×1

Software apps and online services

Vivado Design Suite
AMD Vivado Design Suite
Vitis Unified Software Platform
AMD Vitis Unified Software Platform

Story

Read more

Code

main.c

C/C++
#include <stdio.h>
#include "xaxidma.h"
#include "platform.h"
#include "xil_printf.h"
#include "xparameters.h"
#include "mm2s_controller.h"

XAxiDma AxiDma;

int main()
{
  init_platform();

  XAxiDma_Config *CfgPtr; //DMA configuration pointer
	int Status, Index;
	u8 *TxBufferPtr;

	print("Hello World\n\r");

	TxBufferPtr = (u8 *)TX_BUFFER_BASE;

	// Initialize memory to all zeros
	for(Index = 0; Index < MAX_PKT_LEN; Index ++){
	  TxBufferPtr[Index] = 0x00;
	}

	// Initialize the XAxiDma device
	CfgPtr = XAxiDma_LookupConfig(DMA_DEV_ID);
	if (!CfgPtr) {
		xil_printf("No config found for %d\r\n", DMA_DEV_ID);
		return XST_FAILURE;
	}

	Status = XAxiDma_CfgInitialize(&AxiDma, CfgPtr);
	if (Status != XST_SUCCESS) {
		xil_printf("Initialization failed %d\r\n", Status);
		return XST_FAILURE;
	}

	XAxiDma_IntrDisable(&AxiDma, XAXIDMA_IRQ_ALL_MASK, XAXIDMA_DMA_TO_DEVICE);
	
	double Vout = 0.5;  //desired Vout is 0.5v 
  double Vref = 1.25; //AWG Zmod is in low gain mode, so Vref = 1.25v

  double DAC_code_double;
  u16 DAC_code_u16;
  u16 DAC_code;
  u16 neg_sign = 0x8000;

  DAC_code_double = (Vout/Vref)*(8191);

  if(DAC_code_double < 0){ //if desired Vout is negative, set the sign bit
    DAC_code_u16 = (DAC_code_double * -1);
    DAC_code = DAC_code_u16 << 2;   //shift the 13-bit value up per AWG UG
    DAC_code = ~DAC_code;
    DAC_code = DAC_code | neg_sign; //set the sign bit

  } else {
    DAC_code_u16 = DAC_code_double; //shift the 12-bit value up per AWG UG
    DAC_code = DAC_code_u16 << 2;
  }

  u8 upper_byte = DAC_code >> 8; //shift upper 8 bits of u16 into new u8 var
  u8 lower_byte = DAC_code;      //place lower 8 bits of u16 into new u8 var   

	TxBufferPtr[3] = upper_byte; //AWG Zmod ch1
	TxBufferPtr[2] = lower_byte; //AWG Zmod ch1
  TxBufferPtr[1] = upper_byte; //AWG Zmod ch2
  TxBufferPtr[0] = lower_byte; //AWG Zmod ch2

	Xil_DCacheFlushRange((UINTPTR)TxBufferPtr, MAX_PKT_LEN);
	XAxiDma_Reset(&AxiDma);

	Status = XAxiDma_MM2Stransfer(&AxiDma,(UINTPTR) TxBufferPtr, MAX_PKT_LEN);
	if (Status != XST_SUCCESS){
		xil_printf("XAXIDMA_DMA_TO_DEVICE transfer failed...\r\n");
		return XST_FAILURE;
	}
	
	cleanup_platform();
  return 0;
}

mm2s_controller.c

C/C++
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "xgpio.h"
#include "xaxidma.h"
#include "xstatus.h"
#include "xil_printf.h"
#include "xil_assert.h"
#include "xparameters.h"
#include "mm2s_controller.h"

u32 XAxiDma_MM2Stransfer(XAxiDma *InstancePtr, UINTPTR BuffAddr, u32 Length){

	u32 WordBits;

	// Check scatter gather is not enabled
	if (XAxiDma_HasSg(InstancePtr)){
		xil_printf("Scatter gather is not supported\r\n");
		return XST_FAILURE;
	}

	// Check that the AXI DMA controller has the MM2S port (read channel) enabled in the HW design
	if (!InstancePtr->HasMm2S) {
		xil_printf("MM2S channel is not enabled.\r\n");
		return XST_FAILURE;
	}

	// Check that the requested transfer length is at least 1, but not
	// greater than the max length the DMA's configuration can transfer
	if ((Length < 1) || (Length > InstancePtr->TxBdRing.MaxTransferLen)){
		xil_printf("Invalid MM2S transfer length: %d\r\n", Length);
		return XST_FAILURE;
	}

	// If the engine is doing transfer, cannot submit
	if (!(XAxiDma_ReadReg(InstancePtr->TxBdRing.ChanBase, XAXIDMA_SR_OFFSET) & XAXIDMA_HALTED_MASK)){
		if (XAxiDma_Busy(InstancePtr,XAXIDMA_DMA_TO_DEVICE)){
			xil_printf("MM2S engine is busy\r\n");
			return XST_FAILURE;
		}
	}

	if (!InstancePtr->MicroDmaMode){
		WordBits = (u32)((InstancePtr->TxBdRing.DataWidth) - 1);
	} else {
		WordBits = XAXIDMA_MICROMODE_MIN_BUF_ALIGN;
	}

	if ((BuffAddr & WordBits)){
		if (!InstancePtr->TxBdRing.HasDRE){
			xil_printf("Unaligned transfer without DRE %x\r\n",(unsigned int)BuffAddr);
			return XST_FAILURE;
		}
	}

	// set the run bit
	XAxiDma_WriteReg(InstancePtr->TxBdRing.ChanBase,
			         XAXIDMA_CR_OFFSET,
					 XAxiDma_ReadReg(InstancePtr->TxBdRing.ChanBase,XAXIDMA_CR_OFFSET)| XAXIDMA_CR_RUNSTOP_MASK);

	XAxiDma_WriteReg(InstancePtr->TxBdRing.ChanBase, XAXIDMA_SRCADDR_OFFSET, LOWER_32_BITS(BuffAddr));

	if (InstancePtr->AddrWidth > 32){
		XAxiDma_WriteReg(InstancePtr->TxBdRing.ChanBase, XAXIDMA_SRCADDR_MSB_OFFSET, UPPER_32_BITS(BuffAddr));
	}

	// Writing length in bytes to the buffer transfer length register starts the transfer
	XAxiDma_WriteReg(InstancePtr->TxBdRing.ChanBase, XAXIDMA_BUFFLEN_OFFSET, Length);

	return XST_SUCCESS;
}

mm2s_controller.h

C/C++
#ifndef SRC_MM2S_CONTROLLER_H_
#define SRC_MM2S_CONTROLLER_H_

#define DMA_DEV_ID		    XPAR_AXIDMA_0_DEVICE_ID
#define DMA_BASEADDR        XPAR_AXIDMA_0_BASEADDR
#define DDR_BASE_ADDR		XPAR_PS7_DDR_0_S_AXI_BASEADDR
#define MEM_BASE_ADDR		(DDR_BASE_ADDR + 0x1000000)
#define TX_BUFFER_BASE		(MEM_BASE_ADDR + 0x00100000)
#define RX_BUFFER_BASE		(MEM_BASE_ADDR + 0x00300000)
#define RX_BUFFER_HIGH		(MEM_BASE_ADDR + 0x004FFFFF)
#define V_BUFFER_BASE       (MEM_BASE_ADDR + 0x00500000)

// 0x400/1024 bytes=256 DMA cycles
// 0x80/128 bytes=32 DMA cycles
// 0x20/32 bytes=8 DMA cycles
// 0x08/8 bytes=2 DMA cycles
// 0x04/4 bytes=1 DMA cycles

#define MAX_PKT_LEN		    0x04
#define MIN_PKT_LEN		    0x01
#define NUM_TRANSFERS	    1

u32 XAxiDma_MM2Stransfer(XAxiDma *InstancePtr, UINTPTR BuffAddr, u32 Length);

#endif /* SRC_MM2S_CONTROLLER_H_ */

awg_zmod_port_b.xdc

Plain text
## Syzygy Port B pinout 
#DAC
set_property PACKAGE_PIN Y19 [get_ports {dZmodDAC_Data[0]}]
set_property PACKAGE_PIN Y18 [get_ports {dZmodDAC_Data[1]}]
set_property PACKAGE_PIN AB22 [get_ports {dZmodDAC_Data[2]}]
set_property PACKAGE_PIN AB20 [get_ports {dZmodDAC_Data[3]}]
set_property PACKAGE_PIN AA18 [get_ports {dZmodDAC_Data[4]}]
set_property PACKAGE_PIN AA19 [get_ports {dZmodDAC_Data[5]}]
set_property PACKAGE_PIN Y21 [get_ports {dZmodDAC_Data[6]}]
set_property PACKAGE_PIN Y20 [get_ports {dZmodDAC_Data[7]}]
set_property PACKAGE_PIN V15 [get_ports {dZmodDAC_Data[8]}]
set_property PACKAGE_PIN V14 [get_ports {dZmodDAC_Data[9]}]
set_property PACKAGE_PIN AB15 [get_ports {dZmodDAC_Data[10]}]
set_property PACKAGE_PIN AB14 [get_ports {dZmodDAC_Data[11]}]
set_property PACKAGE_PIN W13 [get_ports {dZmodDAC_Data[12]}]
set_property PACKAGE_PIN V13 [get_ports {dZmodDAC_Data[13]}]
set_property IOSTANDARD LVCMOS18 [get_ports -filter { name =~ dZmodDAC_Data*}]

set_property PACKAGE_PIN W16 [get_ports ZmodDAC_ClkIn]
set_property IOSTANDARD LVCMOS18 [get_ports ZmodDAC_ClkIn]
set_property PACKAGE_PIN W17 [get_ports ZmodDAC_ClkIO]
set_property IOSTANDARD LVCMOS18 [get_ports ZmodDAC_ClkIO]

#DAC SPI
set_property PACKAGE_PIN Y14 [get_ports sZmodDAC_SDIO]
set_property IOSTANDARD LVCMOS18 [get_ports sZmodDAC_SDIO]
set_property DRIVE 4 [get_ports sZmodDAC_SDIO]
set_property PACKAGE_PIN AA14 [get_ports sZmodDAC_CS]
set_property IOSTANDARD LVCMOS18 [get_ports sZmodDAC_CS]
set_property DRIVE 4 [get_ports sZmodDAC_CS]
set_property PACKAGE_PIN AA13 [get_ports sZmodDAC_SCLK]
set_property IOSTANDARD LVCMOS18 [get_ports sZmodDAC_SCLK]
set_property DRIVE 4 [get_ports sZmodDAC_SCLK]

set_property PACKAGE_PIN W15 [get_ports sZmodDAC_SetFS1] 
set_property IOSTANDARD LVCMOS18 [get_ports sZmodDAC_SetFS1]
set_property PACKAGE_PIN Y15 [get_ports sZmodDAC_SetFS2]
set_property IOSTANDARD LVCMOS18 [get_ports sZmodDAC_SetFS2]
set_property PACKAGE_PIN Y13 [get_ports sZmodDAC_Reset]
set_property IOSTANDARD LVCMOS18 [get_ports sZmodDAC_Reset]
set_property PACKAGE_PIN AA22 [get_ports sZmodDAC_EnOut]
set_property IOSTANDARD LVCMOS18 [get_ports sZmodDAC_EnOut]


create_generated_clock -name ZmodDAC_ClkIn -source [get_pins awg_design_i/ZmodAWGController_0/U0/InstDAC_ClkinODDR/C] -divide_by 1 [get_ports ZmodDAC_ClkIn] 
create_generated_clock -name ZmodDAC_ClkIO -source [get_pins awg_design_i/ZmodAWGController_0/U0/InstDAC_ClkIO_ODDR/C] -divide_by 1 [get_ports ZmodDAC_ClkIO]
set_output_delay -clock [get_clocks ZmodDAC_ClkIn] -clock_fall -min -add_delay -1.200 [get_ports {dZmodDAC_Data[*]}]
set_output_delay -clock [get_clocks ZmodDAC_ClkIn] -clock_fall -max -add_delay 0.250 [get_ports {dZmodDAC_Data[*]}]
set_output_delay -clock [get_clocks ZmodDAC_ClkIn] -min -add_delay -1.100 [get_ports {dZmodDAC_Data[*]}]
set_output_delay -clock [get_clocks ZmodDAC_ClkIn] -max -add_delay 0.130 [get_ports {dZmodDAC_Data[*]}]

Credits

Whitney Knitter
172 projects • 1807 followers
All thoughts/opinions are my own and do not reflect those of any company/entity I currently/previously associate with.
Contact

Comments

Please log in or sign up to comment.