Whitney Knitter
Published © GPL3+

Testing FIR Filters in HDL with Zmods on the Eclypse Z7

This project walks through testing FIR filters with real world signals generated by Digilent's AWG Zmod & captured with the Digitizer Zmod.

AdvancedFull instructions provided2 hours958
Testing FIR Filters in HDL with Zmods on the Eclypse Z7

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
Digilent Zmod Digitizer 1430-125: 2-channel 14-bit Digitizer 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

awg_zmod.c

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

u16 voltage_to_DACcodeLow(double Vout){

	double Vref = 1.25;
	double DAC_code_float;
	u16 neg_sign = 0x8000;
	u16 DAC_code_u16, DAC_code;

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

	if(DAC_code_float < 0){
		DAC_code_u16 = (DAC_code_float * -1);
		DAC_code = DAC_code_u16 << 2;
		DAC_code = ~DAC_code;
		DAC_code = DAC_code | neg_sign;
	} else {
		DAC_code_u16 = DAC_code_float;
		DAC_code = DAC_code_u16 << 2;
	}

	return DAC_code;
}

u16 voltage_to_DACcodeHigh(double Vout){

	double Vref = 5.00;
	double DAC_code_float;
	u16 neg_sign = 0x8000;
	u16 DAC_code_u16, DAC_code;

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

	if(DAC_code_float < 0){
		DAC_code_u16 = (DAC_code_float * -1);
		DAC_code = DAC_code_u16 << 2;
		DAC_code = ~DAC_code;
		DAC_code = DAC_code | neg_sign;
	} else {
		DAC_code_u16 = DAC_code_float;
		DAC_code = DAC_code_u16 << 2;
	}

	return DAC_code;
}

awg_zmod.h

C/C++
#ifndef SRC_AWG_ZMOD_H_
#define SRC_AWG_ZMOD_H_

u16 voltage_to_DACcodeLow(double Vout);
u16 voltage_to_DACcodeHigh(double Vout);

#endif /* SRC_AWG_ZMOD_H_ */

dma_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 "dma_controller.h"

#define ENA_ACQ_GPIO_DeviceID 		XPAR_ENA_DIGITIZER_ACQ_DEVICE_ID
#define ENA_ACQ_GPIO_BASEADDR 		XPAR_ENA_DIGITIZER_ACQ_BASEADDR

static XGpio EnaAcqGPIO_Interface_Ptr;
XGpio_Config *EnaAcqGPIO_Interface_Config_Ptr;

int RingIndex, gpio_status;

int config_gpio(){

	EnaAcqGPIO_Interface_Config_Ptr = XGpio_LookupConfig(ENA_ACQ_GPIO_DeviceID);

	gpio_status = XGpio_CfgInitialize(&EnaAcqGPIO_Interface_Ptr, EnaAcqGPIO_Interface_Config_Ptr, EnaAcqGPIO_Interface_Config_Ptr->BaseAddress);

	XGpio_SetDataDirection(&EnaAcqGPIO_Interface_Ptr, 1, 0);
	XGpio_SetDataDirection(&EnaAcqGPIO_Interface_Ptr, 2, 0);

	if(gpio_status != XST_SUCCESS)
	{
		xil_printf("Failed to Initialize EnaAcqGPIO Interface...\n\r");
		return XST_FAILURE;
	}

	return 0;
}

void EnaAcqDigitizer(u8 gpio_value){
	int EnaAcqGPIO_num = 0;
	u32 BitMask = 1 << EnaAcqGPIO_num;

	if (gpio_value == GPIO_DIGITAL_HIGH){
		XGpio_DiscreteSet(&EnaAcqGPIO_Interface_Ptr, 1, BitMask);
	} else {
		XGpio_DiscreteClear(&EnaAcqGPIO_Interface_Ptr, 1, BitMask);
	}
}

void EnaDigitizerDataFIFO(u8 gpio_value){
	int EnaAcqGPIO_num = 0;
	u32 BitMask = 1 << EnaAcqGPIO_num;

	if (gpio_value == GPIO_DIGITAL_HIGH){
		XGpio_DiscreteSet(&EnaAcqGPIO_Interface_Ptr, 2, BitMask);
	} else {
		XGpio_DiscreteClear(&EnaAcqGPIO_Interface_Ptr, 2, BitMask);
	}
}

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

	u32 WordBits;

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

	if (!InstancePtr->HasMm2S) {
		xil_printf("MM2S channel is not enabled.\r\n");
		return XST_FAILURE;
	}

	if ((Length < 1) || (Length > InstancePtr->TxBdRing.MaxTransferLen)){
		xil_printf("Invalid MM2S transfer length: %d\r\n", Length);
		return XST_FAILURE;
	}

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

	// write transfer address
	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;
}

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

	u32 WordBits;
	RingIndex = 0;

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

	if (!InstancePtr->HasS2Mm){
		xil_printf("S2MM channel is not supported\r\n");
		return XST_FAILURE;
	}

	if ((Length < 1) || (Length > InstancePtr->RxBdRing[RingIndex].MaxTransferLen)){
		xil_printf("Invalid S2MM transfer length: %d\r\n", Length);

		u32 Length_Max = InstancePtr->RxBdRing[RingIndex].MaxTransferLen;
		xil_printf("S2MM Length_Max = %d\r\n", Length_Max);

		return XST_FAILURE;
	}

	if (!(XAxiDma_ReadReg(InstancePtr->RxBdRing[RingIndex].ChanBase, XAXIDMA_SR_OFFSET) & XAXIDMA_HALTED_MASK)){
		if (XAxiDma_Busy(InstancePtr,XAXIDMA_DEVICE_TO_DMA)){
			xil_printf("S2MM engine is busy\r\n");
			return XST_FAILURE;
		}
	}

	if (!InstancePtr->MicroDmaMode){
		WordBits = (u32)((InstancePtr->RxBdRing[RingIndex].DataWidth) - 1);
	} else {
		WordBits = XAXIDMA_MICROMODE_MIN_BUF_ALIGN;
	}

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

	// set the run bit (bit 0 in the control reg)
	XAxiDma_WriteReg(InstancePtr->RxBdRing[RingIndex].ChanBase,
					 XAXIDMA_CR_OFFSET,
					 XAxiDma_ReadReg(InstancePtr->RxBdRing[RingIndex].ChanBase, XAXIDMA_CR_OFFSET) | XAXIDMA_CR_RUNSTOP_MASK);

	// write the S2MM destination addr
	XAxiDma_WriteReg(InstancePtr->RxBdRing[RingIndex].ChanBase, XAXIDMA_DESTADDR_OFFSET, LOWER_32_BITS(BuffAddr));

	if (InstancePtr->AddrWidth > 32){
		XAxiDma_WriteReg(InstancePtr->RxBdRing[RingIndex].ChanBase, XAXIDMA_DESTADDR_MSB_OFFSET, UPPER_32_BITS(BuffAddr));
	}

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

	return XST_SUCCESS;
}

dma_controller.h

C/C++
#ifndef SRC_DMA_CONTROLLER_H_
#define SRC_DMA_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)

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

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

#define GPIO_DIGITAL_LOW    0
#define GPIO_DIGITAL_HIGH   1

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

int config_gpio();
void EnaAcqDigitizer(u8 gpio_value);
void EnaDigitizerDataFIFO(u8 gpio_value);

#endif /* SRC_DMA_CONTROLLER_H_ */

main.c

C/C++
#include <math.h>
#include <stdio.h>
#include "sleep.h"
#include "xtime_l.h"
#include "xaxidma.h"
#include "awg_zmod.h"
#include "platform.h"
#include "xil_printf.h"
#include "xparameters.h"
#include "dma_controller.h"

XAxiDma AxiDma;
double degree_convert = M_PI/180;

int main()
{
    init_platform();
    config_gpio();
    EnaAcqDigitizer(GPIO_DIGITAL_LOW);
    EnaDigitizerDataFIFO(GPIO_DIGITAL_LOW);

    print("Hello World\n\r");
    print("Successfully init Hello AWG application\r\n");

    XAxiDma_Config *CfgPtr; //DMA configuration pointer

	int Status, Index;

	// Changed value buffers from u8 to u16 since the data
	// coming from the ADC and going to the DAC is 14-bit
	u8 *TxBufferPtr;
	u8 *RxBufferPtr;

	TxBufferPtr = (u8 *)TX_BUFFER_BASE;
	RxBufferPtr = (u8 *)RX_BUFFER_BASE;

	// Initialize memory to all zeros
	for(Index = 0; Index < MAX_PKT_LEN; Index ++){
		TxBufferPtr[Index] = 0x00;
		RxBufferPtr[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_DEVICE_TO_DMA);
	XAxiDma_IntrDisable(&AxiDma, XAXIDMA_IRQ_ALL_MASK, XAXIDMA_DMA_TO_DEVICE);

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

	// Setup & kick off S2MM channel first
	Status = XAxiDma_S2MMtransfer(&AxiDma,(UINTPTR) RxBufferPtr, MAX_PKT_LEN);
	EnaDigitizerDataFIFO(GPIO_DIGITAL_HIGH);
	EnaAcqDigitizer(GPIO_DIGITAL_HIGH);

	if (Status != XST_SUCCESS){
		xil_printf("XAXIDMA_DEVICE_TO_DMA transfer failed...\r\n");
		return XST_FAILURE;
	}

	Xil_DCacheFlushRange((UINTPTR)RxBufferPtr, MAX_PKT_LEN);

	if (XAxiDma_Busy(&AxiDma,XAXIDMA_DEVICE_TO_DMA) == TRUE){
		xil_printf("S2MM channel is busy 1...\r\n");
	}

	for(double j=100;j>-1;j--){
		for(double i=0;i<361;i=i+1){
			Vout = sin(i*degree_convert);
			DAC_code = voltage_to_DACcodeLow(Vout);

			u8 upper_mask = DAC_code >> 8;
			u8 lower_mask = DAC_code;

			TxBufferPtr[3] = upper_mask; //ch1
			TxBufferPtr[2] = lower_mask; //ch1
			TxBufferPtr[1] = upper_mask; //ch2
			TxBufferPtr[0] = lower_mask; //ch2

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

			Status = XAxiDma_S2MMtransfer(&AxiDma,(UINTPTR) RxBufferPtr, MAX_PKT_LEN);
			if (Status != XST_SUCCESS){
				xil_printf("XAXIDMA_DEVICE_TO_DMA transfer failed...\r\n");
				return XST_FAILURE;
			}

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

			usleep(useconds); //this delay controls the period of the sine wave
		}

		useconds = useconds - 10; //decrement in 10us increments to increase frequency in chirp
	}

	if (XAxiDma_Busy(&AxiDma,XAXIDMA_DEVICE_TO_DMA) == TRUE){
		xil_printf("S2MM channel is busy 2...\r\n");
	}

	if (XAxiDma_Busy(&AxiDma,XAXIDMA_DMA_TO_DEVICE) == TRUE){
		xil_printf("MM2S channel is busy 1...\r\n");
	}

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

	// Setup & kick off S2MM channel first
	Status = XAxiDma_S2MMtransfer(&AxiDma,(UINTPTR) RxBufferPtr, MAX_PKT_LEN);
	if (Status != XST_SUCCESS){
		xil_printf("XAXIDMA_DEVICE_TO_DMA transfer failed...\r\n");
		return XST_FAILURE;
	}

	Xil_DCacheFlushRange((UINTPTR)RxBufferPtr, MAX_PKT_LEN);

	if (XAxiDma_Busy(&AxiDma,XAXIDMA_DEVICE_TO_DMA) == TRUE){
		xil_printf("S2MM channel is busy 3...\r\n");
	}

    EnaAcqDigitizer(GPIO_DIGITAL_LOW);

    DAC_code = voltage_to_DACcodeLow(0);
	upper_mask = DAC_code >> 8;
	lower_mask = DAC_code;
	TxBufferPtr[3] = upper_mask; //ch1
	TxBufferPtr[2] = lower_mask; //ch1
	TxBufferPtr[1] = upper_mask; //ch2
	TxBufferPtr[0] = lower_mask; //ch2
	Xil_DCacheFlushRange((UINTPTR)TxBufferPtr, MAX_PKT_LEN); // NECESSARY after every data change in the buffer

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

	if (XAxiDma_Busy(&AxiDma,XAXIDMA_DEVICE_TO_DMA) == TRUE){
		xil_printf("S2MM channel is busy 4...\r\n");
	}

	if (XAxiDma_Busy(&AxiDma,XAXIDMA_DMA_TO_DEVICE) == TRUE){
		xil_printf("MM2S channel is busy 2...\r\n");
	}

    cleanup_platform();
    return 0;
}

DataStreamPacketizer.v

Verilog
`timescale 1ns / 1ps

module DataStreamPacketizer(
    input clk,    
    input reset_n, 
    
    // AXIS slave interface 
    input [31:0] s_axis_tdata,
    output s_axis_tready,
    input s_axis_tvalid, 
    
    // AXIS master interface 
    output [31:0] m_axis_tdata,
    input m_axis_tready,
    output reg m_axis_tvalid,
    output reg m_axis_tlast
    );
    
    reg [1:0] last_cnt;

    reg [7:0] txr_length_cnt; 
    parameter txr_length = 8'd255; //S2MM transfer length 
    parameter txr_length_cnt_zero = 8'd0; 

    // pass-thru signals 
    assign m_axis_tdata[31:0] = s_axis_tdata[31:0];
    assign s_axis_tready = m_axis_tready;
    
    always @ (posedge clk)
        begin
            if (reset_n == 1'b0)
                begin
                    txr_length_cnt <= txr_length_cnt_zero;
                end
            else if (m_axis_tvalid == 1'b1 && m_axis_tready == 1'b1)
                begin
                    txr_length_cnt <= txr_length_cnt + 1;
                end
            else 
                begin
                    txr_length_cnt <= txr_length_cnt;
                    
                    if (txr_length_cnt == txr_length)
                        begin
                            txr_length_cnt <= txr_length_cnt + 1;
                        end
                end
        end 
    
    always @ (s_axis_tdata)
        begin
            if (txr_length_cnt == txr_length) 
                begin
                    m_axis_tlast <= 1'b1;
                end
            else
                begin
                    m_axis_tlast <= 1'b0;
                end
        end 
    
    always @ (posedge clk)
        begin    
            if (last_cnt != 2'd0)
                begin
                    m_axis_tvalid <= 1'b0;
                    
                    if (m_axis_tready == 1'b1) 
                        begin
                            last_cnt <= 2'd1;
                        end
                    else
                        begin
                            last_cnt <= 2'd0;
                        end
                end
            else if (m_axis_tlast == 1'b1)
                begin
                    m_axis_tvalid <= 1'b0;
                    last_cnt <= 2'd1;
                end
            else
                begin
                    last_cnt <= 2'd0;
                    
                    if (m_axis_tready == 1'b1)
                        begin
                            m_axis_tvalid <= s_axis_tvalid;
                        end
                    else
                        begin
                            m_axis_tvalid <= 1'b0;
                        end
                end
        end 
    
endmodule

Credits

Whitney Knitter
172 projects • 1835 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.