Post Go back to editing

How to write successfully user data to RAM in AD9957

Hi,

It seems that this issue has already been raised at this forum. Unfortunately, even though I tried to follow exactly the suggested steps I couldn't get the data successfully delivered to the memory in AD9957. In particular, I'm not sure how to organize the sequence of signals during RAM loading. I tried as follows:

1. CS = low

2. Send RAM address (0x16)

3. Send N 32-bit words one after another (with I/O_Update issued after each of them)

4. CS = high

Sometimes, I see that the first bit is changed but all the other are unaffected by the above procedure.

I will appreciate any suggestions.

Bartek

  • IO_Updates are not needed during writes to RAM.

    The RAM Segment Registers define the start and end address of RAM read/write operations. You program the RAM Segment Register(s) with the desired start/end addresses.

    You then select the desired RAM segment via the RT pin, where a 0-to-1 transition selects RAM Segment Register 1 and a 1-to-0 transition selects RAM Segment Register 0. The internal RAM controller "knows" how many 32-bit words to write (or read) per the start/end addresses in the selected RAM Segment Register.

    All you have to do is write (or read) the appropriate number of 32-bit words (based on the start/end addresses) to address 0x16.

  • Thanks for the prompt reaction on my inquiry. However, I tried to follow your suggestions without success. Let me just mention that:

    1. single tone works ok

    2. in the interpolating DAC mode, any changes of RAM size affects periodicity of the generated signal but the content is incorrect (noisy)

    Let me share with all my sequence hoping you will find some mistakes:

    /CS = 1
    SCLK = 1
    MASTER_RESET = 1
    MASTER_RESET = 0

    //CFR1 settings:
    I/O_RESET = 1
    I/O_RESET = 0
    /CS = 0
    0x00
    0x01000002
    /CS = 1
    I/O_UPDATE = 0
    I/O_UPDATE = 1
    I/O_UPDATE = 0

    //CFR2 settings:
    I/O_RESET = 1
    I/O_RESET = 0
    /CS = 0
    0x01
    0x00400020
    /CS = 1
    I/O_UPDATE = 0
    I/O_UPDATE = 1
    I/O_UPDATE = 0

    //CFR3 settings:
    I/O_RESET = 1
    I/O_RESET = 0
    /CS = 0
    0x02
    0x113f0100
    /CS = 1
    I/O_UPDATE = 0
    I/O_UPDATE = 1
    I/O_UPDATE = 0

    //PROFILE0 settings:
    I/O_RESET = 1
    I/O_RESET = 0
    /CS = 0
    0x0e
    0x0480000000000000
    /CS = 1
    I/O_UPDATE = 0
    I/O_UPDATE = 1
    I/O_UPDATE = 0

    //RAM Segment Register 0 settings:
    I/O_RESET = 1
    I/O_RESET = 0
    /CS = 0
    0x05
    0x000100030004
    /CS = 1
    I/O_UPDATE = 0
    I/O_UPDATE = 1
    I/O_UPDATE = 0

    RT = 0
    RT = 1

    I/O_RESET = 1
    I/O_RESET = 0
    /CS = 0
    0x16
    0x00
    I/O_UPDATE = 0
    I/O_UPDATE = 1
    I/O_UPDATE = 0
    0x00
    I/O_UPDATE = 0
    I/O_UPDATE = 1
    I/O_UPDATE = 0
    0x00
    I/O_UPDATE = 0
    I/O_UPDATE = 1
    I/O_UPDATE = 0
    0x00
    /CS = 1

    RT = 0
    RT = 1

    //CFR1 settings:
    I/O_RESET = 1
    I/O_RESET = 0
    /CS = 0
    0x00
    0x9f000002
    /CS = 1
    I/O_UPDATE = 0
    I/O_UPDATE = 1
    I/O_UPDATE = 0

    RT = 0
    RT = 1

  • See my comments below imbedded in a copy of your previous post.

    1. single tone works ok

    2. in the interpolating DAC mode, any changes of RAM size affects periodicity of the generated signal but the content is incorrect (noisy) What do you mean by noisy? You should be able to read back from RAM exactly what was written.

     

    Let me share with all my sequence hoping you will find some mistakes:

    General notes regarding SPI transfers...

    1. You can make CS=1 at the top and CS=0 at the bottom (that is, it is not necessary to toggle CS for each SPI operation.

    2. It is not necessary to toggle I/O_RESET for each SPI sequence, as you only need an I/O_RESET if the SPI controller gets lost (an unlikely occurrence).

    /CS = 1
    SCLK = 1
    MASTER_RESET = 1
    MASTER_RESET = 0

    //CFR1 settings:
    I/O_RESET = 1
    I/O_RESET = 0
    /CS = 0
    0x00
    0x01000002
    /CS = 1
    I/O_UPDATE = 0
    I/O_UPDATE = 1
    I/O_UPDATE = 0

    //CFR2 settings:
    I/O_RESET = 1
    I/O_RESET = 0
    /CS = 0
    0x01
    0x00400020
    /CS = 1
    I/O_UPDATE = 0
    I/O_UPDATE = 1
    I/O_UPDATE = 0

    //CFR3 settings:
    I/O_RESET = 1
    I/O_RESET = 0
    /CS = 0
    0x02
    0x113f0100
    /CS = 1
    I/O_UPDATE = 0
    I/O_UPDATE = 1
    I/O_UPDATE = 0

    //PROFILE0 settings:
    I/O_RESET = 1
    I/O_RESET = 0
    /CS = 0
    0x0e
    0x0480000000000000
    /CS = 1
    I/O_UPDATE = 0
    I/O_UPDATE = 1
    I/O_UPDATE = 0

    //RAM Segment Register 0 settings:
    I/O_RESET = 1
    I/O_RESET = 0
    /CS = 0
    0x05
    0x000100030004

    The RAM Segment 0 settings:

    Address Step Rate = 1

    Address End = 0

    Address Start = 0

    Mode = Continuous recirculate

    This implies a RAM address range of 1 (from address 0 to address 0). I'm not 100% certain the RAM controller can handle an address range of 1.
    /CS = 1
    I/O_UPDATE = 0
    I/O_UPDATE = 1
    I/O_UPDATE = 0

    RT = 0
    RT = 1   <== this 0-to-1 transition selects RAM Segment 0

    I/O_RESET = 1
    I/O_RESET = 0
    /CS = 0

    Contrary to the datasheet, I'm fairly certain you need to set the RAM Enble bit prior to reading/writing RAM via the SPI port.
    0x16
    0x00
    I/O_UPDATE = 0
    I/O_UPDATE = 1   <== Do not I/O_Update between write/read operations to/from address 0x16
    I/O_UPDATE = 0
    0x00
    I/O_UPDATE = 0
    I/O_UPDATE = 1
    I/O_UPDATE = 0
    0x00
    I/O_UPDATE = 0
    I/O_UPDATE = 1
    I/O_UPDATE = 0
    0x00
    /CS = 1

    RT = 0   <== this 1-to-0 transition selects RAM Segment 1
    RT = 1   <== this 0-to-1 transition selects RAM Segment 0

    //CFR1 settings:
    I/O_RESET = 1
    I/O_RESET = 0
    /CS = 0   <== Not sure if this was intended, but it prevents the subsequent register write from occurring.

    {Ignore my comment above. It is erroneous. -- edited 8/7/2016}
    0x00
    0x9f000002  <== RAM Enabled, Destination=I/Q path, Mode=Interp. DAC
    /CS = 1
    I/O_UPDATE = 0
    I/O_UPDATE = 1
    I/O_UPDATE = 0

    RT = 0   <== this 1-to-0 transition selects RAM Segment 1
    RT = 1   <== this 0-to-1 transition selects RAM Segment 0

  • Regarding RAM size, I have erroneously copied to the forum RAM0 settings (it was correct). Anyway, now that I have included your suggestions my code is as follows:

     

    /CS = 1
    SCLK = 1
    MASTER_RESET = 1
    MASTER_RESET = 0

    I/O_RESET = 1
    I/O_RESET = 0
    /CS = 0

    //CFR1 settings:
    0x00
    0x01000002
    I/O_UPDATE = 0
    I/O_UPDATE = 1
    I/O_UPDATE = 0

    //CFR2 settings:
    0x01
    0x00400020
    I/O_UPDATE = 0
    I/O_UPDATE = 1
    I/O_UPDATE = 0

    //CFR3 settings:
    0x02
    0x113f0100
    I/O_UPDATE = 0
    I/O_UPDATE = 1
    I/O_UPDATE = 0

    //PROFILE0 settings:
    0x0e
    0x0480000000000000
    I/O_UPDATE = 0
    I/O_UPDATE = 1
    I/O_UPDATE = 0

    //RAM Segment Register 0 settings:
    0x05
    0x0001ffc00004

    I/O_UPDATE = 0
    I/O_UPDATE = 1
    I/O_UPDATE = 0

    RT = 0
    RT = 1

    0x00
    0x9f000002

    I/O_UPDATE = 0
    I/O_UPDATE = 1
    I/O_UPDATE = 0

    0x16

     

    (here I put 1024 of 32 words one after another without I/O updates between)

     

    I/O_UPDATE = 0
    I/O_UPDATE = 1
    I/O_UPDATE = 0

    /CS = 1

    RT = 0
    RT = 1

     

    Now, I see some strange relation between my writing to RAM and the generated signal.

    If I put simply zeros, there is nothing generated and this is obviously ok.

    However, I don't know how to load RAM, let say, with a cosine function. I tried this way:

     

    unsigned short RAMvalue = 0x0000;
    float tmp = 0.0;

    for (int i = 0; i < 1024; i++)
    {
    tmp = cos(8*3.14159*i/1024);
    if (tmp>=0)
       RAMvalue = ((unsigned short) tmp) & 0xefff; //two's complement (positive)
    else{
       tmp = -tmp;
       RAMvalue = ((unsigned short) tmp) | 0x8000; //two's complement (negative)

    }
    RAMdata.bits31_16 = RAMvalue; //I-path
    RAMdata.bits15_0 = 0x0000; //Q-path

    AD9957_send_register_AD9957_RAM(AD9957_RAM, &RAMdata, sizeof(RAMdata));
    }

     

    Unfortunately, it does not work correctly. When I change the frequency in the cosine function above, the shape of pulses (yes, I get pulses instead of cosine) is exactly the same but their repetition is changing only! I'm afraid it has something to do with my erroneous understanding of memory split into I/Q paths sequence in the interpolating DAC mode and the way two's complement is functioning. One way or another, could you propose some exemplary sequency? Thank you in advance.

  • What kind of signal source are you applying to the REF_CLK pins?

    The contents of CFR3 indicate that the system clock PLL is enabled. We need to make sure the system clock is set up properly before trying to do anything else.

    Also, I will retract my earlier comment of "contrary to the datasheet" with regard to RAM Enable. The datasheet makes sense, because enabling the RAM automatically puts it in playback mode. When doing RAM read/write operations via SPI, you don't want the RAM controller trying to play back data to the I/Q signal path. Therefore, you should move your RAM write sequence to precede the write operation to CFR1 that enables RAM.

    By the way, the I/O_Update following the RAM write sequence is unnecessary.

    Regarding your RAM data code...

    The method for generating Q cycles of a cosine wave spanning P samples with N-bit quantization is as follows:

    A = [2^(N-1)] - 1

    y(k) = round{A*cos(Q*2*pi*k/P)}   <== k = 0 .. P-1

    The above yields y as a P-element vector of signed integers with a peak value of A. A is chosen so that the quantized cosine wave is symmetric about zero. I think when you cast y as an int in C it is represented in twos-complement form.

    Each 32-bit RAM word constitutes both an I and Q value, with I allocated to the 16 MSBs and  Q allocated to the 16 LSBs. However, because you are using Interpolating DAC mode, only the I values matter. So, the RAM words are the y values left-shifted by 16.

  • I have applied 33MHz quartz (ILCX13) connected to REF_CLK pins. It works ok with single tone, so I would not expect any issues related to that. I have applied your suggestions and I get the signal at ca. 349 MHz for Q = 1 in the proposed formula for y(k):

    short y = 0x0000;

    short Q = 1;

    for (int k = 0; k < 1024; k++)
    {

    y = round(32767*cos(Q*2*3.14159*k/1024));

    RAMdata.bits31_16 = RAMvalue;
    RAMdata.bits15_0 = 0x0000;

    send(...)

    }

    However, if I change anything (e.g. Q = 2; or sin instead of cos) I get nothing at the output. Why such a simple change from cos() to sin() spoil the generation? By the way, I don't see any impact of PLL settings, like VCO range, on the generated signal. How, in that case, control the frequency of the arbitrary signal?

  • According to your programming sequence, CFR3 = 0x113f0100.

    If so, then I cannot see how the PLL is functioning properly as the last byte indicates the N-divider is set for N=0, which is not a valid value.

    Without first knowing the system clock frequency, I cannot validate the 349MHz output frequency.

    Using the PLL requires some extra work:

    1. Determining the proper loop filter component values
    2. Selecting the correct internal VCO
    3. Selecting the proper charge pump current

    It would be easier if you could use a high frequency generator (~1GHz) at the REF_CLK input, as this would eliminate the nuances associated with the PLL. If you elect to use a generator, be sure the XTAL_SEL (pin 95) is Logic 0 and CFR3[15:8]=0xC0.

    Changing the formula variables or switching from cosine to sine should have no impact on the ability to produce a signal. Something else is going on.

    Can you put the 1024 32b RAM words in a file and post it?

    It would be most helpful if you provide the actual 32b words (hex or binary) as written to RAM. This will allow me to at least validate your RAM data (one less "unknown" to deal with).

    By the way, do you read back and verify the contents of RAM after you've written them?

    It would be helpful to know that you can read back what you've written, as it eliminates another "unknown".

  • I don't see any impact of the N-divider on the functioning of the whole system in the interpolating DAC mode, although that impact should be expected. Anyway, I have changed CFR3 to 0x123f0142 (N = 33). These settings allow generating single tone at 280 MHz (or any other) successfully, so I hope I don't need to change them. By the way, my PLL operates correctly. I have confirmed that in two ways:

    1. using single tone

    2. reading back the content of several registers (it proofs that my reading algorithm works ok)

    However, when I try to write anything to RAM (e.g. 128 32-bit words), the read from RAM consists of 32-bit words of 0xFFFFFFFF. For technical reasons, I can't copy that data at the moment as I communicate with my DDS chip via STM32 microcontroller.

    There is for sure something wrong with my RAM load procedure, which looks as follows:

    short y = 0x0000;

    short Q = 1;

    spi_send_register(0x16); //send RAM Register address

    for (int k = 0; k < 128; k++)
    {

    y = round(32767*cos(Q*2*3.14159*k/128));

    RAMdata.bits31_16 = y;
    RAMdata.bits15_0 = 0x0000;

    spi_send_register(RAMdata); //send consecutive 32-bit words

    }

    I/O_UPDATE = 0
    I/O_UPDATE = 1
    I/O_UPDATE = 0

    /CS = 1

  • RAMdata appears to be a structure with two components: bits31_16 and bits15_0.

    I see no problem with bits15_0, as these are being forced to 0x0000.

    However, y is a signed number. It must, in fact, be a 16b signed integer, otherwise there will be an inconsistency. Because the "y" operation includes floats and integers, you should verify that the y values reflect the 16b hex values you expect them to be. You could be suffering from a data format/casting issue.

  • I think I may have found a possible problem in your RAM code, but I'm not 100% sure because I'm not privy to all of your code. I am guessing that the function, spi_send_register(x), generates a SPI sequence that formats an instruction byte with the appropriate read/write bit and address followed by the data, x. If so, that is a problem.

    Writing/reading RAM involves a single instruction byte followed by ALL of the desired RAM content. So, you must build an array of RAM values and send them all together (with a single preceding instruction byte).