# Designing a PI filter for a Costas Loop

I am currently trying to implement a Costas Loop in order to demodulate a BPSK signal. Here is the reference block diagram I have been using to design the loop:Costas Loop Block Diagram

I originally tried to make this all in VHDL code, but because I ended up using almost entirely IP cores, I decided to make it in Vivado's IP integrator:Vivado IP Integrator Block Diagram

From my research online, I found the low pass filter for the I and Q arm can be a simple SRRC filter using a FIR design. The cutoff frequency just needs to be small enough to eliminate the summation of the NCO frequency and the BPSK frequency, while not eliminating the difference between these frequencies. I used python to come up with good filter taps. Here is a screenshot of the IP core settings I was using as well as the frequency response:Low Pass Filter(LPF) Settings

For the NCO, I used Xilinx's DDS core to act as a phase accumulator and SIN/COS LUT. The DDS core will generate two tones that are 90 degrees out of phase which I use for the I/Q arms. Here are the settings I used for the core:DDS Settings; I don't have enough reputation on stack overflow for 9 screenshots so I will just say that the core is in phase increment streaming mode and phase offset is turned off.

Lastly, comes the loop filter, which I believe is the heart of the issue, but I will get into that in a bit. I have read several papers on what the appropriate design for this filter is; some say it has to be a FIR LPF, some say it has to be an IIR LPF, some say it should be PID contol filter, but most say it has to be a proportional + integral(PI) filter. Going off of that here is the reference design I using:PI Loop Filter Block Diagram. Regarding the integrator path, I handled the delay by making that adder synchronous (added a clock signal to it). I should also note that all the Adder/Subtracter blocks in Vivado are set to the Adding mode, not the subtracting mode.

That is pretty much the whole design, now I will finish up with simulation set up and results. Here is my test bench for generating a BPSK signal and providing gain values for the PI filter: 

LO_inst : dds_compiler_1
PORT MAP (
aclk => clk_t,
s_axis_phase_tvalid => '1',
s_axis_phase_tdata => x"0000" & x"1999", --zero phase offset, set BPSK frequency to fs/5
m_axis_data_tvalid => VCO_valid,
m_axis_data_tdata => VCO_OUT
);

Demodulator_inst : Demod_wrapper
port map (
Data_IN => Data_IN_t(15 downto 8), --uses SIN half of signal
alpha => "0010" & "0000" & "0000" & "0000", --divide by 2^16 to get gain value
beta => "0010" & "0000" & "0000" & "0000", --divide by 2^16 to get gain value
clk => clk_t
);

BPSK_mod : process (bit_stream, VCO_OUT)
begin
case (bit_stream) is
when '0' =>
Data_IN_t <= VCO_OUT;
when '1' =>
Data_IN_t <= not VCO_OUT;
when others =>
Data_IN_t <= VCO_OUT;
end case;
end process BPSK_mod;

toggle_bit : process (clk_t) is
begin
if rising_edge(clk_t) then
if bit_cnt < 200 then
bit_cnt <= bit_cnt + 1;
else
bit_cnt <= 0;
bit_stream <= not bit_stream;
end if;
end if;
end process;


The DDS module in the test bench is set up the exact same way as the IP core except I added the ability to do a phase offset to show phase locking. Every 200 clock cycles (clock = 250MHz by the way) the signal switches 180 degrees in phase. Here are the simulation waveforms:Sim screenshot 1 Sim screenshot 2 Sim screenshot 3

From these results, I can see the SRRC filters are doing their job by eliminating those faster oscillation present on the arms and I know the DDS module is acting the way it should as well. This leads me to think this is a loop filter issue. In the papers I've read the output of the loop filter seem to increase until it got to a nice steady state value where it was frequency and phase locked.

Any insight as to why this is happening would be greatly appreciated. I hope I articulated the problem well, but if I didn't please let me know what wasn't clear.

Update1: Per Dan's helpful instructions I tried a few things. I started of by setting the integrator and proportional gain to zero, I then increased the proportional gain, while leaving I = 0 (in my code I call it beta), until I was able to see the system phase lock match the signal and the NCO. I was able to get it to lock with the following gain values: I = 0 and P = -2^-4

Demodulator_inst : Demod_wrapper
port map (
Data_IN => Data_IN_t(15 downto 8),
alpha => "1111" & "0000" & "0000" & "0000", --divide by 2^16 to get gain value
beta => "0000" & "0000" & "0000" & "0000", --divide by 2^16 to get gain value
clk => clk_t
);


I also moved some of the waveforms around and updated the block diagram I was using so it would be easier to follow. Here is the updated block diagram and the waveforms showing it locks:

You might notice it takes a while to lock, but when I increase the P gain it locks faster. I wasn't sure if I was was supposed to find the minimum absolute value of P before it locks. I also didn't notice anything that looked like gain dependent error so that might be an indication I did something wrong.

Now comes the part where I am still kind of stuck. The next step I did was increase the frequency of the input signal until I see the loop filter output start to oscillate. Originally the input frequency was 25MHz. It began to oscillate when the input frequency was ±84kHz which seems pretty small to me.

I pulled back the offset frequency by half and started to increment my integrator gain. Although even at the smallest value I = 2^-16 the output of the loop filter diverges:

I think something about my approach might be wrong, so any advice would be appreciated.

• a PI compensator is an IIR filter, while the converse is not true. That being said, using an FIR as a compensator is something new for me. Perhaps, an FIR with a high DC gain is almost the same as a P controller. – Ben Feb 25 at 3:11
• Oh, I see. Thanks for letting me know. I believe the integrator is needed for frequency matching, so I don't think a P controller/FIR filter would work. But I could be wrong. – Jake Feb 25 at 3:29
• It would be helpful if you could label your waveforms with the same node names as in your Costas Loop block diagram--- it's not easy to follow what is what. – Dan Boschen Feb 25 at 3:45
• ok I followed the plots and they seem to make sense-- one thing you can try is to manually tune your filter by starting with I = 0 (turn of the integrator path basically) and get it to lock with P alone (it will have a gain dependent error, not the way you want to run finally but just to see if it will converge- be careful that you don't have a wrong sign on your feedback; easiest is to invert it if it doesn't correctly lock. Once that works increase to threshold of oscillation and then back it off to half value, then slowly bring up I while observing your settling time /overshoot etc – Dan Boschen Feb 25 at 3:59
• Also consider plotting the NCO output right alongside the input signal or even superimposed on the same plot- it will be easier to see if it is working / converging properly – Dan Boschen Feb 25 at 5:01