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?