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