DAC LTC2624 core- Verilog HDL


Datasheet: Datasheet_2604fd.pdf



This Verilog core was designed to control the DAC LTC®LTC2624 which is a quad 12-bit 2.5V to 5.5V rail-to-rail voltage output DACs. The acronym suggests, SPI is a serial communication protocol designed by the company Motorola. It is widely used today for data transmission between different devices in full-duplex mode, either between processors and peripherals or between peripherals. This serial protocol is being extensively employed in the industrial electronics field due to its ability to transmit and receive data at high speeds while using a minimal number of pins (usually 4) in its interface (Figure 1).



Figure 1- SPI Interface


As seen in Figure 1, this type of communication generally uses 4 wires to transmit and receive data from a Master (CPU) to a Slave (Peripheral). Various configurations are possible. Nowadays, many peripherals or communication cards, including SPI displays, touchscreen drivers, ADCs (Analog-to-Digital Converters), DACs (LTC2624 Digital-to-Analog Converters), Ethernet communication modules, Wi-Fi, and 3G modules, use this protocol. This demonstrates the protocol's importance, and it's essential for students in electronics-related engineering fields to understand how it works and be able to implement it in their class projects (labs), research, or theses.


In this type of serial communication, you can transmit up to 32-bit words per frame. When compared to conventional UART-RS232 transmission, it surpasses it by providing four times more information per frame. This allows users to save time on data masking (splitting data into multiple words) when transmitting information.


SPI interface description (see Figure 1):


CS (Chip Select): In every SPI transmission, the CS (Chip Select) pin is used to indicate when a data transmission is about to begin. Typically, this pin is held high when data transmission or reception is not required.


SCK (Serial Clock): This is the heart of SPI transmission because it determines, through its rising or falling edges, when to send each bit of the word from the master to the receiver. In other words, it is the clock (CLK) that specifies when to load the output or input data into the system, considering MOSI and MISO.


MOSI (Master Out Slave In): This pin is responsible for loading the data bit by bit from the word to be sent on each rising or falling edge of SCK (this depends on the SPI type, refer to datasheets). Typically, data is sent from Most Significant Bit (MSB) to Least Significant Bit (LSB), and there are as many SCK cycles as there are data bits, depending on the frame and SPI type.


MISO (Master In Slave Out): Contrary to MOSI, this pin is responsible for receiving data sent by the Slave as data is being transmitted according to the rising or falling edge of SCK.


Figure 2 - SPI Transmission for DAC (LTC2624), taken from the SPARTAN-3A/3AN Starter Kit Board User Guide - UG334 (v1.0) May 28, 2007.


As shown in Figure 2, there are 4 wires in different states according to the pin descriptions provided earlier: DAC_CS(CS), SPI_MOSI(MOSI), SPI_CLK(SCK), and DAC_OUT(MISO). In this case, you can observe that DAC_CS indicates when the transmission and reception of information are about to begin, in this instance from the peripheral device, the LTC2624. Furthermore, it is apparent that after CS is activated, on a clock edge, the most significant bit of the word to be sent is loaded into MOSI, and subsequently, on the rising edge of SCK, it is loaded into the DAC. On the falling edge, some information from the DAC is received via MISO.


DAC (LTC2624) from the Spartan 3A Starter Kit:

This Digital-to-Analog Converter (DAC) has various applications in the field of electronics, ranging from control to communications. It allows us to convert digital information into analog signals (variable voltages) for signal generation, including AM and FM modulations, sine waves, and more.

The converter is manufactured by Linear Technology and features 4 output channels that we can control to generate voltage signals with a 12-bit unsigned precision per channel (this depends on the VREF, see Equation 1). The LTC2624 receives 32-bit words per frame, where 4 bits indicate the channel through which the information should be sent, another 4 bits specify the type of command or special operation (such as outputting information, adjusting the DAC offset, etc.), and the desired 12-bit unsigned digital value of the voltage as per Equation 1. The remaining bits are null values that facilitate synchronization with the peripheral (see Figure 3).


Equation 1 - Output Voltage as a function of the Reference Voltage and the 12-bit Data loaded via SPI..



Figure 3 - Description of the characteristic data frame of the DAC (LTC2624), taken from the SPARTAN-3A/3AN Starter Kit Board User Guide - UG334 (v1.0) May 28, 2007.


As can be seen in Figure 3, the word that forms the command and the data sent to the DAC consist of 32 bits distributed into different packets and orders. Among these, 12 bits are of the "Don't care" type and enclose the 12-bit packet (unsigned digital voltage value), a[3:0] (the address to which the digital voltage value is to be sent), and c[3:0] (the command or instruction to be sent to the DAC). For more information on the commands the DAC can receive, you should refer to the attached LTC2624 datasheet (see Table 1).


Table 1 - List of commands and addresses for the LTC2624 DAC.


Figure 4 - SPI Interface for DAC LTC2624, taken from the SPARTAN-3A/3AN Starter Kit Board User Guide - UG334 (v1.0) May 28, 2007.


In Figure 4, you can see the SPI interface between the connection of the FPGA Spartan 3A on the board and the DAC peripheral. The description of each pin of this interface can be found in the DAC datasheet. In this case, you can observe that pin AB13 of the FPGA carries the DAC_CLR, which should be kept high to ensure that the chip receives information and remains active. The rest of the pins have already been explained (refer to Figure 2).

To build an FSM that sends the necessary information to the DAC, follow these steps:

1. First, determine the logical steps to create the state machine based on the behavior of the SCK, CS, MOSI, and MISO signals. Since you want to transmit a voltage value to each DAC channel and not receive information via MISO, you will use CS, SCK, and MOSI.

Remember to ensure that the DAC_CLR signal is appropriately set to keep the DAC active.

2. Define the states of the FSM that correspond to these logical steps, such as an IDLE state where you wait for the start signal, states for loading data onto MOSI, transitioning SCK to transmit data, and checking for the completion of data transmission.

3. Specify the transitions between these states based on the actions of CS, SCK, and MOSI. For example, when CS goes low, you can transition from IDLE to the state where you start transmitting data.

4. Implement the logic within each state to control the actions of CS, SCK, and MOSI as per the steps you've determined.

5. Ensure that the DAC_CLR signal is properly controlled to activate the DAC when necessary.

By following these steps and considering the behavior of the SPI signals, you can construct the FSM needed to send data to the DAC as described in Figure 2.



Inputs and outputs of our SPI interface


Figure 5 - Design of the SLAVE module for SPI transmission to the LTC2624 DAC.


As seen in Figure 5, first, you should organize in your mind how the final design will look. In this case, you need a clock for the state machine to function (CLK_50Mhz), input data (DataIn[31:0] - Data to be serialized), a "start" signal (indicating when the module will start sending data), SCK, MOSI, CS, and a "finish" signal (indicating when the module has finished transmitting data and is ready to transmit another frame).

With this, you can begin to create a state diagram based on the behavior of SCK, MOSI, CS, and the "start" and "finish" signals.

As shown in Figure 6, the state machine to be designed consists of 5 states. In the IDLE state, it waits for the "start" signal to begin the data transmission from DataIn[31:0]. It also indicates that "CS" should be set low. In the FIRST state, the data to be sent (bit by bit from DataIn[31-count]) is loaded onto the "MOSI" pin. In the SECOND state, "SCK" is raised to send the data loaded by "MOSI." In the THIRD state, it sums and compares how many data bits have been sent via the "MOSI" channel using a counter (Count). If the sum exceeds Nbits (in this case, 32 bits), it transitions to the FOURTH state, where the "CS" pin is deactivated, and the counter is reset to return to the IDLE state (triggering a "finish" pulse) and wait for a new command. In cases where the "count" does not exceed "Nbits," it returns to the FIRST state to repeat the process as many times as Nbits indicates. The state of each pin and internal register of the FSM can be seen in Table 2.

Figure 6 - Generalized SPI FSM State Diagram.


Table 2 - State Table, Logical Level of Each Pin, and Internal Register of the FSM

In Table 2, you can see the logical state of the output pins CS, SCK, and FINISH, as well as the logical state of the UP register, which is responsible for incrementing the count to serialize DataIn[31-Count] from the Most Significant Bit (MSB) to the Least Significant Bit (LSB).

If you were to perform an annotated simulation of this FSM, assuming Nbits is equal to 3 (to keep the simulation reasonably short), you would have the following:

Figure 7 - Annotated Simulation of the SPI FSM, assuming Nbits is 3.


In Figure 7, you can observe the behavior of the CS, SCK, FINISH, and UP signals with respect to each rising edge (Posedge) of the CLK_50MHz clock that the FSM operates with.


- Count: This is an internal register responsible for counting the number of bits transmitted through the MOSI channel to the DAC.


- CS: It is active in the IDLE state, waiting to transition to the FIRST state when a synchronous pulse of the "START" signal arrives with the CLK_50MHz. CS also functions as a "reset" for the internal data sent counter, Count.


- FINISH: It remains active in IDLE, indicating that the state machine is waiting for an order to serialize data. When it finishes sending the 32-bit data, the FINISH signal returns to 1 in IDLE, indicating the completion of the frame transmission.


- SCK: It is activated in the SECOND state, validating the data loaded in MOSI (DataIn[31-Count]). At the beginning, when Count is 0, the MSB DataIn[31] is sent. In this case, Nbits was assumed to be 3, which means that only the packets DataIn[31 to 29] are sent at each posedge of SCK.


- UP: It is activated in the THIRD state, providing a pulse to an internal counter responsible for counting the number of data (Count) sent via MOSI. In the annotated simulation, data is sent until Nbits is greater than or equal to 3. If you observe the annotated simulation (see Figure 7), there were only 3 UP rising edges, and automatically, after the third edge, it transitions to the FOURTH state due to the conditions in the state diagram (see Figure 6).


Note: In the FSM design, it's necessary to send 32 Nbits. The simulation (see Figure 7) was set up with 3 Nbits to simplify the iterative process of the state diagram logic (see Figure 6).



Design FSM using Verilog

The hardware design created using Verilog should correspond to the state diagram (see Figure 6) and the annotated simulation (see Figure 7).


For the FSM design, a type of recursive state encoding will be used, combining the logical states of each output and register in the state. This significantly simplifies the amount of code required to generate the FSM.


Taking into account Table 2, the hardware module named "SLAVE_SPI" (see Figure 5) is designed


Figure 8 - Initial Verilog HDL Header, taking into account the initial design proposed.


In Figure 8, the header of the FSM module responsible for serializing and sending the DataIn[31:0] data to the LTC2624 DAC is shown. As you can observe, some module parameters or constants were defined, each comprising 7 bits, which contain the state of each of the signals seen in the simulation (Finish, UP, SCK, and CS), as well as the state number of the machine (STATE). In this design, the state of each signal is included in the state parameters to independently vary the way they change as the state transitions. Figure 9 provides a detailed view of what it means to design a recursive FSM using the same state to vary the logical value of the signals that make up the sequential logic to correctly send each signal (Finish, UP, SCK, CS, reset, etc.) at the appropriate times.


Figure 9 - Pin assignment and state of each signal according to the state, using recursive FSM design.


The STATE_SPI register is initialized in the IDLE state, and the Count_data counter register is initialized to 6'd0. Each bit of STATE_SPI is directly assigned to the outputs through internal wires. If you observe, "Finish" is directly assigned to the MSB bit (STATE_SPI[6]), "SCK" is directly assigned to STATE_SPI[4], "CS" is directly connected to STATE_SPI[3], and "MOSI" is directly connected to a selector that chooses the bit to send based on the value of Count_data[5:0]. Including the logical value of each output in the same STATE allows for an organized state machine design, reduces the logic needed for the design, and ensures that each signal varies according to the FSM state (in this case, STATE_SPI[6:0]).

In Figure 10, you can see the HDL code for the respective state machine and the internal counter responsible for counting the data sent via the MOSI channel. As you can see, the code is quite concise and easy to understand thanks to the use of a Recursive FSM design.

In Figure 11, the simulation performed in the Xilinx ISE 9.2 Hardware Synthesizer is shown, and it is compared with the one in Figure 7 (Annotated Simulation). The only difference between the two is the use of an Nbits value of 32 (Figure 11) and 3 (Figure 7). The value of MOSI varies according to DataIn[31-Count_data], and you can observe that at each SCK edge, data is sent to the LTC2624 DAC from MSB to LSB. The simulation also confirms that the "finish" pulse is only active in the IDLE state and that the "Start" input pulse activates the FSM to begin the data sending process once CS goes low.


Figure 10 - SPI state machine and internal counter.


Figure 11 - Simulation of FSM SPI_SLAVE.v.


Implementation of the SPI_SLAVE module for sending voltage values to the channels of the LTC2624 DAC: 

The FSM has been designed to send 32-bit data using SPI, thanks to which orders can be sent to the DAC LTC2624. Among these orders is the ability to send voltage values to each analog channel of the DAC.


To create the DataIn[31:0] frame needed to send a voltage value to channel 0 of the DAC, you must consider the information provided in Table 1, Figure 3, the datasheet, and Equation 1.




Assuming you want to send 1 volt to channel 0 of the DAC, how would you construct the 32-bit frame to be sent using the designed SPI_SLAVE module?


Considering Equation 1, with a fixed reference voltage of 3.3V (see Figure 4), DATA[11:0] (the digital value of the voltage) is equal to:


DATA[11:0] = (1V / 3.3V) * (2^12 - 1)


Calculate DATA[11:0] using this equation, and you will have the 12-bit value to be included in the 32-bit frame, along with the appropriate channel and command bits as per the LTC2624 datasheet and Table 1.



The command used to select the ADDRESS to which you want to send the voltage value and the COMMAND can be found in Table 1.


Since you want to send the data to channel 0, you would:


- Set the ADDRESS bits (A3, A2, A1, A0) to 0000, indicating channel 0.

- Choose the appropriate COMMAND based on your requirements (e.g., to update the DAC output).


By following the specifications in Table 1, you can construct the 32-bit frame to send 1 volt to channel 0 of the DAC using the SPI_SLAVE module.


ADDRESS[3:0] =             4’d0       =             4’b0000


And since the COMMAND you want to send is to write to the DAC's output the voltage value and update it (as shown in Table 1), it should be:


COMMAND[3:0] =           4’d3       =             4’b0011


Now, what would be the frame to be sent through DataIN[31:0]?


DataIN[31:0]={8’b00000000, COMMAND[3:0] , ADDRESS[3:0], DATA[11:0] , 4’b0000};


In Figure 12, you can see the Verilog code for sending a value of 1 volt to each channel of the DAC, with the channel varying based on the switches on the Spartan Starter Kit 3A (SW[3:0]). In the code, DAC_CLR is set high to ensure that the data transmitted by the SPI to the DAC LTC2624 is valid. Additionally, the `.Start_in` input in the Verilog module instantiation is set to 1'b1 to trigger automatic data transmission.


Note: If you want to send data synchronously to the SPI_SLAVE module, you should synchronize the Start_in signal with the CLK_50Mhz signal to prevent data loss. Also, consider Finish_out to determine the appropriate times for sending data.


Figure 12 - Example code to send 1 volt to each channel of the DAC, changing the channel with SW[3:0].


Proposed exercise: 

Design a small state machine called "MASTER_SPI" that controls the SPI_SLAVE module and is responsible for sending voltage values to the 4 DAC channels independently and synchronously using 12-bit inputs, as shown in Figure 13:

Figure 13 - Proposed Exercise


Dac1[11:0], Dac2[11:0], Dac3[11:0], and Dac4[11:0] are the digital voltage values to be sent to each corresponding channel of the LTC2624 DAC.


Asyncronous_signal: This is the channel sampler, and it should be internally synchronized with the CLK signal to ensure no data loss.


Note: The exercise simply involves distributing the input values of Dac1, 2, 3, and 4 using the module created in this tutorial, taking into account the Start and Finish signals of the design (be sure to consider signal synchronization with respect to the used CLK).


The Verilog code for the SPI_SLAVE module can be found:



`timescale 1ns / 1ps
// Company: Semillero ADT - UPB Bucaramanga
// Engineer: Holguer A Becerra Daza
// Create Date:    20:32:24 05/10/2012 
// Design Name: 
// Module Name:    SPI_SLAVE 
// Project Name: 
// Target Devices: 
// Tool versions: 
// Description: 
// Dependencies: 
// Revision: 
// Revision 0.01 - File Created
// Additional Comments: 
module SPI_SLAVE(CLK_50Mhz,CS,MOSI,SCK,DataIn,Start_in,Finish_out);
parameter IDLE=	7'b1______0___0___1__000;
parameter FIRST=	7'b0______0___0___0__001;
parameter SECOND=	7'b0______0___1___0__010;
parameter THIRD=	7'b0______1___0___0__011;
parameter FOURTH=	7'b0______0___0___1__100;

input CLK_50Mhz;
input [31:0]DataIn;
input Start_in;
output CS;
output MOSI;
output SCK;
output Finish_out;

reg [6:0]STATE_SPI=IDLE;
reg [5:0]Count_data=6'd0;

wire Finish=STATE_SPI[6];
wire UP_Count=STATE_SPI[5];
wire Reset_count=STATE_SPI[3];

assign Finish_out=Finish;
assign CS=CS_OUT;
assign SCK=SCK_OUT;
assign MOSI=DataIn[31-Count_data];

always@(posedge UP_Count,posedge Reset_count)
	if(Reset_count) Count_data<=6'd0;
	else Count_data<=Count_data+1'b1;

always@(posedge CLK_50Mhz)
	IDLE: begin
				if(Start_in==1'b1) STATE_SPI<=FIRST;
				else STATE_SPI<=IDLE;
	THIRD: begin 
				if(Count_data<32) STATE_SPI<=FIRST;