Analog Out, Verilog 1

This page has a simple version of what you need to test the Analog Devices AD5541A 16 bit 1 Ms/s DAC mounted on a Digilent Pmod DA3. I am using a NandLand GoBoard, but this code can be easily ported to other environments.

Top level module

///////////////////////////////////////////////////////////////////////////////
// File downloaded from https://halverscience.net
///////////////////////////////////////////////////////////////////////////////
// Demonstrates and test Digilent's Pmod DA3 digital-to-analog converter
// using the Nandland.com GoBoard
// The DAC is to be plugged into the 2nd port of the Pmod connector, i.e., the
// 2nd row of holes.
// The DAC is fed various 16 bit patterns depending on which Make_Test_Waveform module 
// you use.  The sample rate is 1 MHz.
// Created 7/2020 by Peter Halverson.
/////////////////////////////////////////////////////////////////////////////// 

`default_nettype none       // Use this to find undeclared wires. Optional.

module DAC_Test_Top
  (input  wire i_Clk,         // Main Clock, 25 MHz
   output wire o_Segment1_A,  // Segment1 is upper digit, Segment2 is lower digit
   output wire o_Segment1_B, output wire o_Segment1_C, output wire o_Segment1_D,
   output wire o_Segment1_E, output wire o_Segment1_F, output wire o_Segment1_G,
   output wire o_Segment2_A, output wire o_Segment2_B, output wire o_Segment2_C,
   output wire o_Segment2_D, output wire o_Segment2_E, output wire o_Segment2_F,
   output wire o_Segment2_G,   
   output wire o_LED_1, output wire o_LED_2, output wire o_LED_3, output wire o_LED_4,
   output wire io_PMOD_1,     // Wiring to the ADC board ~ chip select (active low)
   //input  io_PMOD_2,        // data from ADC 0
   output  wire io_PMOD_2,    // temporary mirror of data to DAC
   input  wire io_PMOD_3,     // data from ADC 1  (not used)
   output wire io_PMOD_4,     // clock to read out data
   output wire io_PMOD_7,     // Wiring to the DAC board ~ chip select (active low)
   output wire io_PMOD_8,     // data to DAC 0
   output wire io_PMOD_9,     // data to ADC 1  (not used)
   output wire io_PMOD_10,    // clock to read out data
   input  wire i_Switch_1,    // Wires to the switches.  Not used now, but useful for debugging.
   input  wire i_Switch_2, input  wire i_Switch_3, input  wire i_Switch_4,
   output wire o_VGA_HSync,   // VGA connector
   output wire o_VGA_VSync,   // I'm using this to trigger the oscilloscope (3rd row, 2nd hole)
                              // Ground is 2nd row, last hole, to the right.
   output wire o_VGA_Red_0, output wire o_VGA_Red_1, output wire o_VGA_Red_2,
   output wire o_VGA_Grn_0, output wire o_VGA_Grn_1, output wire o_VGA_Grn_2,
   output wire o_VGA_Blu_0, output wire o_VGA_Blu_1, output wire o_VGA_Blu_2   
  );

  wire w_Sample_Clock;              // 1 MHz.  This is the max speed of the DA3 module.
  Make_Sample_Clock
  Make_Sample_Clock_inst(
    .i_Clk(i_Clk),                // 25 MHz clock goes in
    .o_Sample_Clock(w_Sample_Clock)  // 1 MHz comes out
  );

  wire [15:0] w_Waveform;

  // -------------------------Here is where you can choose which waveform.----------------
  Make_Test_Waveform_Sawtooth   
  //Make_Test_Waveform_Growing_Squarewave
  Make_Test_Waveform(
    .i_Clk(i_Clk),
    .i_Sample_Clock(w_Sample_Clock),
    .o_Waveform(w_Waveform),
    .o_Scope_Trigger(o_VGA_VSync)
  );

// GoBoard's PMOD connector
// Looking at the connector into the holes, you see two rows of 6 holes:
// oooooo          1st row
// oooooo          2nd row
// 
// The first row, which is for PMOD module 1, going left-to-right is
// Pin 6, VCC (+3.3 V)
// Pin 5, Ground
// Pin 4, Port 1 pin 4  (usually a clock signal to the module)
// Pin 3, Port 1 pin 3  (can be data or other signal)
// Pin 2, Port 1 pin 2  (usually data to or from the module)
// Pin 1, Port 1 pin 1  (usually a select or "activate" signal, active low, to the module)
// 
// The second row, which is for PMOD module 2, going left-to-right is
// Pin 12, VCC (+3.3 V)
// Pin 11, Ground
// Pin 10, Port 2 pin 4  (usually a clock signal to the module)
// Pin  9, Port 2 pin 3  (can be data or other signal)
// Pin  8, Port 2 pin 2  (usually data to or from the module)
// Pin  7, Port 2 pin 1  (usually a select or "activate" signal, active low, to the module)

                          // Comments apply to the Pmod DA3.  (The Pmod DA2 is different)
  wire w_Pmod_Port2_Pin1; // ~CS, Pull low when moving data into the DAC
  wire w_Pmod_Port2_Pin2; // DIN Data stream going to the DAC.  16 bits.
  wire w_Pmod_Port2_Pin3; // ~LDAC, Falling edge or holding low activates the DAC
  wire w_Pmod_Port2_Pin4; // SCLK, DAC Clock.  Data bits are latched on rising edge of this clock.

  Send_To_DAC_Pmod_DA3 Send_To_DAC_Pmod_DA3_inst(
    .i_Clk(i_Clk),
    .i_Data_Valid(w_Sample_Clock),
    .i_DAC_Data(w_Waveform),
    .o_notCS(w_Pmod_Port2_Pin1),
    .o_notLDAC(w_Pmod_Port2_Pin3),
    .o_Sclk(w_Pmod_Port2_Pin4),
    .o_Serial_Data(w_Pmod_Port2_Pin2)
  );

  // Wires to the DAC
  assign io_PMOD_7  = w_Pmod_Port2_Pin1;//Chip select signal, active low
  assign io_PMOD_1  = w_Pmod_Port2_Pin1;//DUPLICATE for easier connection to oscilloscope

  assign io_PMOD_8  = w_Pmod_Port2_Pin2;//Serial data out to DAC
  assign io_PMOD_2  = w_Pmod_Port2_Pin2;//DUPLICATE for easier connection to oscilloscope

  assign io_PMOD_9  = w_Pmod_Port2_Pin3;//~LDAC,  When pulled down, activates the DAC. 
                                        //(See AD5541A data sheet)

  assign io_PMOD_10 = w_Pmod_Port2_Pin4;//25 MHz clock to clock in the serial data stream
  assign io_PMOD_4 = w_Pmod_Port2_Pin4; //DUPLICATE for easier connection to oscilloscope

   
  assign o_Segment1_A = 1;  //Turn off the 7 segment displays
  assign o_Segment1_B = 1; assign o_Segment1_C = 1; assign o_Segment1_D = 1;
  assign o_Segment1_E = 1; assign o_Segment1_F = 1; assign o_Segment1_G = 1;
  assign o_Segment2_A = 1; assign o_Segment2_B = 1; assign o_Segment2_C = 1;
  assign o_Segment2_D = 1; assign o_Segment2_E = 1; assign o_Segment2_F = 1;
  assign o_Segment2_G = 1;

  assign o_LED_1 = 0;   // Turn off the LEDs  (because they are annoying)
  assign o_LED_2 = 0; assign o_LED_3 = 0; assign o_LED_4 = 0;

endmodule

`default_nettype wire        // Sets the default back to the usual.

Make_Test_Waveform_Sawtooth.

Module to make a sawtooth that uses all 65536 DAC codes.

///////////////////////////////////////////////////////////////////////////////
// File downloaded from https://halverscience.net
///////////////////////////////////////////////////////////////////////////////
// Generates a sawtooth wave to test the Digilent Pmod DA3 DAC.
// Since the sawtooth tries all 2**16 cominations at a 1 MHz sample rate
// the output frequency will be 1 MHz / 2**16 = 15.258 Hz
// Author: Peter Halverson 7/2020
///////////////////////////////////////////////////////////////////////////////

`default_nettype none       // Use this to find undeclared wires. Optional.

module Make_Test_Waveform_Sawtooth (
  input wire i_Clk,
  input wire i_Sample_Clock,
  output wire [15:0] o_Waveform,
  output wire o_Scope_Trigger
  );

  reg [15:0] r_Waveform;          
  always @(posedge i_Sample_Clock) begin
    r_Waveform <= r_Waveform + 1;    // This will make a sawtooth
  end //always 

  assign o_Waveform = r_Waveform;
  assign o_Scope_Trigger = r_Waveform[15]; // Used to sync my oscilloscope for a stable display

endmodule

`default_nettype wire        // Sets the default back to the usual.

Make_Test_Waveform_Growing_Squarewave

Module to produces a 500 kHz squarewave, sawtooth modulated.

The benefit of this test is that it will detect issues with rapidly changing DAC codes and fast analog slewrates. (It helped me discover a subtle bug in my Verilog having to do with unspecified state machine states.)

The problem with this test is that it drives digital oscilloscopes crazy. Beware of aliasing!

///////////////////////////////////////////////////////////////////////////////
// File downloaded from https://halverscience.net
///////////////////////////////////////////////////////////////////////////////
// Generates a 500 KHz square wave modulated by a sawtooth wave to test the 
// Digilent Pmod DA3 DAC.
// Since the sawtooth tries all 2**16 cominations at a 1 MHz sample rate
// the output frequency will be 1 MHz / 2**16 = 15.258 Hz
// Author: Peter Halverson 7/2020
///////////////////////////////////////////////////////////////////////////////

`default_nettype none       // Use this to find undeclared wires. 

module Make_Test_Waveform_Growing_Squarewave (
  input wire i_Clk,
  input wire i_Sample_Clock,
  output wire [15:0] o_Waveform,
  output wire o_Scope_Trigger
  );

  reg [15:0] r_Waveform;     
  reg [15:0] r_Counter;
  always @(posedge i_Sample_Clock) begin
    r_Counter <= r_Counter + 1;  // This will make a sawtooth
    if (r_Counter[0] == 1'b1) begin
      //r_Waveform <= 16'hffff;
      r_Waveform[15] <= 1'b1;
      r_Waveform[14:0] <= r_Counter[15:1];  // This makes a sawtooth that begins at 
                                            // 1000 0000 0000 0000 and ends at 1111 1111 1111 1111
    end else begin
      //r_Waveform <= 16'h0000;
      r_Waveform[15] <= 1'b0;
      r_Waveform[14:0] <= ~r_Counter[15:1];  // This makes a sawtooth that begins at 
                                            // 0111 1111 1111 1111 and ends at 0000 0000 0000 0000
   end
  end //always 

  assign o_Waveform = r_Waveform;
  assign o_Scope_Trigger = r_Counter[15];   // Used to sync the oscilloscope for a stable display

endmodule

`default_nettype wire        // Sets the default back to the usual.

Send_To_DAC_Pmod_DA3.

Module to serialize and send data to the DAC.

///////////////////////////////////////////////////////////////////////////////
// File downloaded from https://halverscience.net
///////////////////////////////////////////////////////////////////////////////
// Serializes and sends data to an Analog Devices AD5541 16 bit DAC mounted on a
// Digilent Pmod DA3 digital-to-analog converter board.
// Created 7/2020 by Peter Halverson.
///////////////////////////////////////////////////////////////////////////////

// AD5541 max clock frequency is 50 MHz. We're using 25 MHz.
// DAC expects a serial data stream 16 bits long. MSB first.

`default_nettype none       // Use this to find undeclared wires. 

module Send_To_DAC_Pmod_DA3 (
  input wire i_Clk,
  input wire i_Data_Valid,
  input wire [15:0] i_DAC_Data,
  output reg o_notCS,    // Hold low to make chip accept data bits
  output wire o_notLDAC, // Either hold low to make chip output on receipt of last bit, or
                         // pull low when you want the output to happen.
  output wire o_Sclk,    // DAC accepts data bits on RISING edges of this clock.  
  output reg o_Serial_Data
  );

  parameter IDLE      = 1'b0;// State Machine states (Only two; I could have used an if statement)
  parameter SEND_BITS = 1'b1;

  reg        r_SM_Main; 
  reg [14:0] Output_Buffer;
  reg [4:0]  Bit_Count;

  always @(posedge i_Clk) begin
    case (r_SM_Main)
      IDLE : begin
        if (i_Data_Valid == 1'b1) begin
          Bit_Count <= 4'b0000;
          o_notCS <= 1'b0;   // Start the DAC
          o_Serial_Data <= i_DAC_Data[15];    //Send the MSB immediately
          Output_Buffer <= i_DAC_Data[14:0];  //Will send the remaining bits later
          r_SM_Main <= SEND_BITS;
        end else begin
          o_notCS <= 1'b1;   // Keep the DAC inactive
          o_Serial_Data <= 1'b0;     // Just make the data out be zero (not needed?)
          r_SM_Main <= IDLE;// Not needed? Well, yes it IS needed.  In some cases the code won't
                            // work without it.  WHY??? 
                            // Example: try the 500 kHz squarewave modulated by
                            // 15 Hz Sawtooth to see it not work.
        end //if
      end // case: IDLE
      SEND_BITS : begin
        if (Bit_Count == 4'b1111) begin  
          o_Serial_Data <= Output_Buffer[14];// Send last bit, which has been shifted to MSB
          //The last bit has been sent
          o_notCS <= 1'b1;  // Deactivate the DAC
          r_SM_Main <= IDLE;
        end else begin
          o_notCS <= 1'b0;  // Keep the DAC active  (Not needed?)
          o_Serial_Data <= Output_Buffer[14];   // Send bit 14 of Output_Buffer
          Output_Buffer <= Output_Buffer << 1;  // Shift output buffer left one bit; moves next
          // bit into bit 14.
          Bit_Count <= Bit_Count + 1;
          r_SM_Main <= SEND_BITS;// Not needed?  Well, yes it's needed.  In some cases the code won't
                                 // work without it.  WHY???
        end //if
      end //case: SEND_BITS
    endcase
  end //always 
  assign o_Sclk = ~i_Clk;  //Invert the clock because the chip registers data bits on rising edges.
  assign o_notLDAC = 1'b0; //Holding low to get immediate DAC output
endmodule

`default_nettype wire      // Sets the default back to the usual.

Make_Sample_Clock

Module to make a 1 MHz clock for the DAC

///////////////////////////////////////////////////////////////////////////////
// File downloaded from https://halverscience.net
///////////////////////////////////////////////////////////////////////////////
// Takes the 25 MHz clock and divides it down to whatever low frequency is needed.
// Note the output is NOT symmetric.  It is a 40 ns pulse (1/25 MHz = 40 ns) once every period.
// Author: Peter Halverson 7/2020
///////////////////////////////////////////////////////////////////////////////

`default_nettype none       // Use this to find undeclared wires. Optional.

module Make_Sample_Clock (
  input wire i_Clk,          // 25 MHz clock in
  output reg o_Sample_Clock
);
  parameter DIVIDER = 25;    // This gives 1 MHz

  reg [4:0] r_Count;            //5 bits needed
  always @(posedge i_Clk) begin
    if (r_Count == DIVIDER-1) begin
      r_Count <= 0;
      o_Sample_Clock <= 1;
    end else begin
      r_Count <= r_Count + 1;
      o_Sample_Clock <= 0;
    end
  end //always 

endmodule

`default_nettype wire        // Sets the default back to the usual.