CORDIC algorithm based DDS quadrature wave synthesis

CORDIC algorithm is implemented here in hardware (FPGA). This verilog code is able to generate quadrature carriers (I-Q) for the purpose of modulation. It is also used for finding the value of non-linear functions like sine(), cosine() and some hyperbolic functions.

One of the most popular way to generate sine waveform is using Direct digital synthesis. But how a digital circuit calculate the non-linear functions ? It is using either pre-feeded look-up table or using a famous algorithm named as CORDIC. In CORDIC, the angle rotation information is stored in a look up table (ROM using assign keyword in Verilog). It is based upon method similar to bisection method in numerical analysis.

Digital circuits have the limitation of not understanding decimals. If a circuit is made to interpret decimal representation than it would take a lot of space and power. So the idea is to do the operation in binary (the value of sine) and the angle is compared to the desired value.

It is digital method so number of bits matter. It takes total n-cycles for n-bits precision.

This code currently supports 10-bit output. However it can be modified to generate desired resolution output. It is non-pipelined code. I am also trying for a pipelined code. That would allow the CORDIC core to generate sampling frequency equal to master clock frequency.

DDS block with CORDIC as a sub-block

`timescale 1ns / 1ps

module DDS(
input mainClock,
output [9:0]cosine,
output [9:0]sine,
output reg [2:0]stateMachineCounter,
output reg angleLatch,
output strobe
);

reg [9:0]angleCounter;

cordic3 c0(.mainClock(clock),.angleInput(angleCounter),.angleLatch(angleLatch),.cosine(cosine),.sine(sine),.strobe(strobe));
clockDivider c1(.mainClock(mainClock),.clock(clock));

initial 
begin
	angleLatch <= 1'b0;
	angleCounter <= 10'b0;
	stateMachineCounter <= 3'b000;
end

always@(posedge clock) // (mainClock / 256)
begin
	stateMachineCounter <= stateMachineCounter + 1'b1;
	casez(stateMachineCounter)
	3'b000: 
	begin
		angleLatch <= 1'b0; // (clock / 8)
		angleCounter <= angleCounter + 2'b11;
	end
	3'b1??,3'b01?,3'b001:
	begin
		angleLatch <= 1'b1;
	end
	endcase
end

endmodule

Main CORDIC module

`timescale 1ns / 1ps

module cordic3 #(parameter ANGLE_RESOLUTION = 10, OUTPUT_RESOLUTION = 10, LUT_SIZE = 3, DC_OFFSET = 10'd512)(
input angleLatch,
input mainClock,
input [ANGLE_RESOLUTION-1:0]angleInput, //from 2's compliment representation e.g., 230 = (230/360)*1024 = 1010001110 , in other ways it represent angle from -pi to pi in signed 2's compliment form 
output reg [OUTPUT_RESOLUTION-1:0]cosine,
output reg [OUTPUT_RESOLUTION-1:0]sine,
output strobe
);

reg [ANGLE_RESOLUTION-1:0]angleAccumulator;
reg [LUT_SIZE-1:0]lookupTableIndex;
reg [OUTPUT_RESOLUTION-1:0]xNew; //10-bit number, MSB representing sign-bit //signed 2's compliment form
reg [OUTPUT_RESOLUTION-1:0]yNew; //10-bit number, MSB representing sign-bit //signed 2's compliment form
reg [ANGLE_RESOLUTION-1:0]angleInputReg;

wire [ANGLE_RESOLUTION-1:0]comparator;
wire [OUTPUT_RESOLUTION-1:0]yShifted; //signed 2's compliment
wire [OUTPUT_RESOLUTION-1:0]xShifted; //signed 2's compliment
wire [ANGLE_RESOLUTION-1:0]lookupTable[0:7]; //minimum angle value is 0.4476 at 8th place //ANGLE-3 for 1'st quadrant only
wire [ANGLE_RESOLUTION-1:0]rotationDirection;

assign yShifted = yNew >>> lookupTableIndex; //arithmetic shift right
assign xShifted = xNew >>> lookupTableIndex; //arithmetic shift right
assign strobe = (lookupTableIndex == 3'b000);
//assign rotationDirection = (angleAccumulator >= angleInputReg);
assign rotationDirection = angleInputReg - angleAccumulator;

always@(posedge mainClock)
begin
	if(angleLatch == 1'b0)
	begin
		case(angleInput[9:8])
		2'b00:
		begin
			angleInputReg <= angleInput;
		end
		2'b11:
		begin
			angleInputReg <= -angleInput; // in case angle is 270(deg) = 10'b1100000000 then -angle = 0100000000
		end
		2'b01:
		begin
			angleInputReg <= 10'b1000000000 - angleInput; //180 - angleInput
		end
		2'b10:
		begin
			angleInputReg <= 10'b1000000000 + angleInput; //180 + angleInput
		end
		endcase
		xNew <= 10'b01_0011_0110; //511 * 0.6073 = 310
		yNew <= 10'b00_0000_0000;
		lookupTableIndex <= 3'b000;
		angleAccumulator <= 10'b00_0000_0000;
	end
	else
	begin
			xNew <= rotationDirection[ANGLE_RESOLUTION-1] ? (xNew + yShifted):(xNew - yShifted);
			yNew <= rotationDirection[ANGLE_RESOLUTION-1] ? (yNew - xShifted):(yNew + xShifted);
			angleAccumulator <= rotationDirection[ANGLE_RESOLUTION-1] ? (angleAccumulator - lookupTable[lookupTableIndex]):(angleAccumulator + lookupTable[lookupTableIndex]);
			lookupTableIndex <= lookupTableIndex + 3'b1;
			//$write(angleAccumulator);
	end
end

always@(posedge mainClock)
begin
	if(lookupTableIndex == 3'b111)
	begin
		case(angleInput[9:8]) //selecting the sign of functions using ASTC rule and adding an offset for +3.3V R2R DAC
		2'b00:
		begin
			cosine <= xNew + DC_OFFSET;
			sine <= yNew + DC_OFFSET;
		end
		2'b11:
		begin
			cosine <= xNew + DC_OFFSET;
			sine <= -yNew + DC_OFFSET;
		end
		2'b01:
		begin
			cosine <= -xNew + DC_OFFSET;
			sine <= yNew + DC_OFFSET;
		end
		2'b10:
		begin
			cosine <= -xNew + DC_OFFSET;
			sine <= -yNew + DC_OFFSET;
		end
		endcase
	end
end

//lookupTable for the angle				   //bin	//decimal(deg)	//exact(deg)
assign lookupTable[0] = 10'b0010000000;	//128 //45.0000		//45.0000
assign lookupTable[1] = 10'b0001001011;	//75	//26.3672		//26.5651
assign lookupTable[2] = 10'b0000100111;	//39	//13.7109		//14.0362
assign lookupTable[3] = 10'b0000010100;	//20	//7.0313			//7.1250
assign lookupTable[4] = 10'b0000001010;	//10	//3.5156			//3.5763
assign lookupTable[5] = 10'b0000000101;	//5	//1.7578			//1.7899
assign lookupTable[6] = 10'b0000000010;	//2	//0.7031			//0.8952
assign lookupTable[7] = 10'b0000000001;	//1	//0.3516			//0.4476

endmodule

Clock divider

`timescale 1ns / 1ps

module clockDivider #(parameter DIV_LENGTH = 8)(
input mainClock,
output clock
    );

initial
begin
	counter <= 8'b0;
end
assign clock = counter[DIV_LENGTH-1];

reg [DIV_LENGTH-1:0]counter;

always@(posedge mainClock)
begin
	counter <= counter + 1'b1;
end

endmodule

Test bench for DDS block

`timescale 1ns / 1ps

module test_DDS;

	// Inputs
	reg mainClock;

	// Outputs
	wire [9:0] cosine;
	wire [9:0] sine;
	wire strobe;
	wire [2:0]stateMachineCounter;
	wire angleLatch;
	
	// Instantiate the Unit Under Test (UUT)
	DDS uut (
		.mainClock(mainClock),  
		.cosine(cosine), 
		.sine(sine), 
		.strobe(strobe),
		.stateMachineCounter(stateMachineCounter),
		.angleLatch(angleLatch)
	);

	initial begin
		// Initialize Inputs
		mainClock = 0;

		// Wait 100 ns for global reset to finish
		
		// Add stimulus here

	end
      
		always
		begin
			#5 mainClock = ~mainClock;
		end
		
endmodule