Showing results for 
Show  only  | Search instead for 
Did you mean: 
Registered: ‎02-20-2019

Design Help: HLS, AXI Streams, TLAST (and the DDS core)

Hi Folks,

    I'm trying to use the DDS core to teach myself about a combination of HLS, axi streams, and stream to DMA processes. At a high level my goal is to generate a python script (using the PYNQ ecosystem) to program multiple DDS cores with phase increments and capture a user defined number of samples from them to PS memory. It outputs a free running AXI stream without TLAST. I'm working with the ZCU111 Zynq Ultrascale+ development board in 2018.3. (As an aside, I'm an Astronmer and very much still learning so even the simplest pointers are very much appreciated!)

Option 1) Use the DDS IP directly in HLS with top level function arguments for phase increment and number of samples desired and generate TLAST internally and only start counting (I think) from whenever the AXIS consumer (an AXI DMA AXIS_S2MM for now) indicates its first TREADY. I don't think I fully grasp the last part yet. This is my preferred approach as longer term I'm going to need to use several DDS cores in concert. Unfortunately, it is a non-starter: the DDS core is instantiated through a static template that does not support phase increment programmability. To quote UG902 p259 "XIP_DDS_PINCPOFF_FIXED fixes PINC at generation time, and PINC cannot be changed at run time. This is the only value supported." Crud. I've used this approach and simulation in HLS to verify that I understand how to configure the DDS core, but that is about it.

static hls::DDS<config1> dds1;  //No provision to alter the config., phase_channel);  //No provision to send phase increment updates.

Option 2) Generate an HLS module that takes an AXILITE argument for number of samples and outputs a TLAST after so many ticks. I'm not clear on how to synchronize the reset of the internal counter with the kickoff of the AXI DMA S2MM transfer. I'd thought about adding in input of TREADY but that would be using something that, i think, isn't really appropriate as back pressure signals would be misconstrued. 

void option2(unsigned int *num_samples, bool *tready_in, bool *tlast) {
#pragma HLS INTERFACE ap_ctrl_none port=return
#pragma HLS INTERFACE ap_none port=tlast
#pragma HLS INTERFACE ap_none port=tready_in
#pragma HLS INTERFACE s_axilite port=num_samples

    static unsigned int trigger_val = *num_samples>0 ? *num_samples-1 : *num_samples;
    static unsigned int counter_val = 0;
    static bool run=false;

    trigger_val = *num_samples>0 ? *num_samples-1 : *num_samples;
    run = (!run && tready_in) || run;

    if (run) {
        if (counter_val==trigger_val) {
        } else {

Option 3) Generate an HLS module that takes an AXILITE  argument for the number of samples and an axistream and outputs an axistream, but maintains an internal decrementing and sets TLAST on the output when the counter reaches 0 (and stops forwarding the stream data, I think). Here I'm running into a similar issue as I would have in 1: How to indicate that the DMA is wanting to receive those samples and that the internal counter should be reset to its starting value and forwarding begun?

#include "ap_axi_sdata.h"

void example(ap_axis<32,4,5,6>& IN, ap_axis<32,4,5,6>& OUT, unsigned int *forward_count){
#pragma HLS INTERFACE axis port=IN
#pragma HLS INTERFACE axis port=OUT
#pragma HLS INTERFACE s_axilite port=forward_count
#pragma HLS INTERFACE ap_vld port=forward_count
#pragma HLS INTERFACE ap_ctrl_none port=return
    static unsigned int count=*forward_count;
    if (count==0) {
        count=*forward_count; //ap_vld causes stall until forward_count is written again
    } else {
        count--; =;
        OUT.keep = IN.keep;
        OUT.strb = IN.strb;
        OUT.user = IN.user;
        OUT.last = count==0; =;
        OUT.dest = IN.dest;

Option 4) Something else entirely, maybe an AXI Stream FIFO?

 Any feedback is much appreciated, this is all quite new to me.


Tags (4)
1 Reply
Registered: ‎12-29-2020

Did you manage to get this working?

A lot of tutorials describe having both an input and output axi stream, where TREADY, TLAST, whatever are just copied over from the IN to the OUT. I'm really struggling to get this working without having an input stream. My IP just generates bits, and I want to push those to PS (not feeding back to my IP).

0 Kudos