shabaz made a Software Defined Radio (SDR) Experiment Board. One of the components is a digital quadrature oscillator.
It's a circuit with 4 PWM outputs that are each 90° shifted.
In Shabaz' blog, the oscillator is made with flip-flops, and controlled by an external function generator.
Because it's the Summer of FPGAs, I'm proposing 2 FPGA based alternatives.
They work identical, generate the same frequency (input / 4), but the approach is different.
One works by replaying a predefined output pattern on the 4 outputs, with one clock delay for each output.
The second one by calculating the first channel based on a counter value, and left shifting the data through the outputs.
The entity is identical for the two approaches:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
entity quadrature_oscillator is
Port (
i_clk: in std_logic;
i_nReset: in std_logic;
o_outputs: out std_logic_vector (3 downto 0)
);
end quadrature_oscillator;
This is the first implementation.
The pattern used to define the outputs is 0011001.
Each of the pins continuously cycles through a (different!) range of 4 bits of the pattern, on the cadence of the clock.
pin 0 from 3 to 6: 1100
pin 1 from 2 to 5: 0110
pin 2 from 1 to 4: 0011
pin 3 from 0 to 3: 1001
They do this at the same time, in parallel.
You can see that this generates an output where each of the pins is 90° shifted.
architecture Behavioral of quadrature_oscillator is
signal s_pattern: std_logic_vector (6 downto 0) := "0011001";
signal s_counter: natural range 0 to 3 := 0;
begin
process (i_clk, i_nReset)
begin
-- if i_nReset = '0' then
-- s_counter <= 0;
-- els
if rising_edge(i_clk) then
if s_counter = 3 then
s_counter <= 0;
else
s_counter <= s_counter + 1;
end if;
o_outputs(0) <= s_pattern(s_counter + 3);
o_outputs(1) <= s_pattern(s_counter + 2);
o_outputs(2) <= s_pattern(s_counter + 1);
o_outputs(3) <= s_pattern(s_counter);
end if;
end process;
end Behavioral;
The second implementation takes a different approach.
The data is the output vector is shifted 1 position to the left. That generates 90° phase shift.
I only have to take care that I set the LSB correct each time. For that, I use the MSB of the counter, inversed.
The counter is 2 bits. It cycles 00 -> 01 -> 10 -> 11.
If you take the MSB each time, inversed, you get 1100.
That's the identical as the output of pin 0 in the first implementation. The left-shift does the rest of the magic for the other 3 pins.
architecture Behavioral of quadrature_oscillator is
signal s_counter: UNSIGNED (1 downto 0) := "00";
signal s_buffer: std_logic_vector (3 downto 0) := (others => '0');
begin
process (i_clk, i_nReset)
begin
if i_nReset = '0' then
s_counter <= "00";
s_buffer <= (others => '0');
elsif rising_edge(i_clk) then
if s_counter = "11" then
s_counter <= (others => '0');
else
s_counter <= (s_counter) + 1;
end if;
s_buffer <= s_buffer (2 downto 0) & not(s_counter(1));
o_outputs <= s_buffer;
end if;
end process;
end Behavioral;
I've attached Vivado project and Jupyter notebook. Implementation #2 is active, approach #1 is commented out.
The VHDL for #2 is adapted, see https://www.element14.com/community/groups/fpga-group/blog/2021/08/04/learning-xilinx-zynq-a-quadrature-oscillator#comme… .
Latest source on github: https://gist.github.com/jancumps/0a1b2692ddfd6edc79e8487adc66539e
Which design do you prefer? Or do you have an alternative?

Top Comments