element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • Community Hub
    Community Hub
    • What's New on element14
    • Feedback and Support
    • Benefits of Membership
    • Personal Blogs
    • Members Area
    • Achievement Levels
  • Learn
    Learn
    • Ask an Expert
    • eBooks
    • element14 presents
    • Learning Center
    • Tech Spotlight
    • STEM Academy
    • Webinars, Training and Events
    • Learning Groups
  • Technologies
    Technologies
    • 3D Printing
    • FPGA
    • Industrial Automation
    • Internet of Things
    • Power & Energy
    • Sensors
    • Technology Groups
  • Challenges & Projects
    Challenges & Projects
    • Design Challenges
    • element14 presents Projects
    • Project14
    • Arduino Projects
    • Raspberry Pi Projects
    • Project Groups
  • Products
    Products
    • Arduino
    • Avnet Boards Community
    • Dev Tools
    • Manufacturers
    • Multicomp Pro
    • Product Groups
    • Raspberry Pi
    • RoadTests & Reviews
  • Store
    Store
    • Visit Your Store
    • Choose another store...
      • Europe
      •  Austria (German)
      •  Belgium (Dutch, French)
      •  Bulgaria (Bulgarian)
      •  Czech Republic (Czech)
      •  Denmark (Danish)
      •  Estonia (Estonian)
      •  Finland (Finnish)
      •  France (French)
      •  Germany (German)
      •  Hungary (Hungarian)
      •  Ireland
      •  Israel
      •  Italy (Italian)
      •  Latvia (Latvian)
      •  
      •  Lithuania (Lithuanian)
      •  Netherlands (Dutch)
      •  Norway (Norwegian)
      •  Poland (Polish)
      •  Portugal (Portuguese)
      •  Romania (Romanian)
      •  Russia (Russian)
      •  Slovakia (Slovak)
      •  Slovenia (Slovenian)
      •  Spain (Spanish)
      •  Sweden (Swedish)
      •  Switzerland(German, French)
      •  Turkey (Turkish)
      •  United Kingdom
      • Asia Pacific
      •  Australia
      •  China
      •  Hong Kong
      •  India
      •  Korea (Korean)
      •  Malaysia
      •  New Zealand
      •  Philippines
      •  Singapore
      •  Taiwan
      •  Thailand (Thai)
      • Americas
      •  Brazil (Portuguese)
      •  Canada
      •  Mexico (Spanish)
      •  United States
      Can't find the country/region you're looking for? Visit our export site or find a local distributor.
  • Translate
  • Profile
  • Settings
FPGA
  • Technologies
  • More
FPGA
Blog Learning Xilinx Zynq: a Quadrature Oscillator - 2 implementations
  • Blog
  • Forum
  • Documents
  • Quiz
  • Events
  • Polls
  • Files
  • Members
  • Mentions
  • Sub-Groups
  • Tags
  • More
  • Cancel
  • New
Join FPGA to participate - click to join for free!
  • Share
  • More
  • Cancel
Group Actions
  • Group RSS
  • More
  • Cancel
Engagement
  • Author Author: Jan Cumps
  • Date Created: 4 Aug 2021 8:32 PM Date Created
  • Views 5363 views
  • Likes 13 likes
  • Comments 23 comments
  • zynq
  • xilinx
  • vivado
  • summer of fpgas
  • pynq
Related
Recommended

Learning Xilinx Zynq: a Quadrature Oscillator - 2 implementations

Jan Cumps
Jan Cumps
4 Aug 2021

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.

image

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?

 

Pynq - Zync - Vivado series
Add Pynq-Z2 board to Vivado
Learning Xilinx Zynq: port a Spartan 6 PWM example to Pynq
Learning Xilinx Zynq: use AXI with a VHDL example in Pynq
VHDL PWM generator with dead time: the design
Learning Xilinx Zynq: use AXI and MMIO with a VHDL example in Pynq
Learning Xilinx Zynq: port Rotary Decoder from Spartan 6 to Vivado and PYNQ
Learning Xilinx Zynq: FPGA based PWM generator with scroll wheel control
Learning Xilinx Zynq: use RAM design for Altera Cyclone on Vivado and PYNQ
Learning Xilinx Zynq: a Quadrature Oscillator - 2 implementations
Learning Xilinx Zynq: a Quadrature Oscillator - variable frequency
Learning Xilinx Zynq: Hardware Accelerated Software
Automate Repeatable Steps in Vivado
Learning Xilinx Zynq: Try to make my own Accelerated OpenCV Function - 1: Vitis HLS
Learning Xilinx Zynq: Try to make my own Accelerated OpenCV Function - 2: Vivado Block Design
Learning Xilinx Zynq: Logic Gates in Vivado
Learning Xilinx Zynq: Interrupt ARM from FPGA fabric
Learning Xilinx Zynq: reuse and combine components to build a multiplexer
PYNQ version 2.7 (Austin) is released
PYNQ and Zynq: the Vitis HLS Accelerator with DMA training - Part 1: Turn C++ code into an FPGA IP
PYNQ and Zynq: the Vitis HLS Accelerator with DMA training - Part 2: Add the Accelerated IP to a Vivado design
PYNQ and Zynq: the Vitis HLS Accelerator with DMA training - Part 3: Use the Hardware Accelerated Code in Software
PYNQ and Zynq: the Vitis HLS Accelerator with DMA training - Deep Dive: the data streams between Accelerator IP and ARM processors
Use the ZYNQ XADC with DMA part 1: bare metal
Use the ZYNQ XADC with DMA part 2: get and show samples in PYNQ
VHDL: Convert a Fixed Module into a Generic Module for Reuse

 

Attachments:
quad_osc_jupyter.zip
quadrature_oscillator.zip
  • Sign in to reply

Top Comments

  • Jan Cumps
    Jan Cumps over 3 years ago in reply to jc2048 +4
    These are the GUI settable parameters: Then there's a bespoke Clocking Wizard IP that can be attached to clock signals in fabric: I've not tested it yet, I just put one on the block design and checked…
  • Jan Cumps
    Jan Cumps over 3 years ago +3
    The Vivado / Zynq / Pynq artifacts: Block diagram: Constraints: Jupyter notebook: I've used one of the Zync fabric clocks as input. Any clock works, and output will be clock/4. Here, the input clock was…
  • fmilburn
    fmilburn over 3 years ago +3
    Impressive that you did this so quickly and with so little code
  • Jan Cumps
    Jan Cumps over 3 years ago in reply to Jan Cumps

    It works.

     

    image

     

    With a clock of 100 MHz, and an 8 bit divider, I have between 48.8284 KHz and 12.5001 MHz.

    First step approx. 371 Hz, last one 6.25 MHz.

     

    There are many VHDL runtime configurable clock dividers available online. I've written one myself for this exercise:

     

    library IEEE;
    use IEEE.STD_LOGIC_1164.ALL;
    use IEEE.NUMERIC_STD.ALL;
    
    entity variable_clock is
        Port ( 
          clk_i    : in  STD_LOGIC;
          resetn_i : in  STD_LOGIC;
          ticks_i  : in  STD_LOGIC_VECTOR (7 downto 0);
          clk_o    : out STD_LOGIC
        );
    end variable_clock;
    
    architecture Behavioral of variable_clock is
      signal s_counter: natural range 0 to 2**ticks_i'length-1 := 0;
      signal s_clk_out: STD_LOGIC := '0';
    begin
    
    process (clk_i, resetn_i)
    begin
      if resetn_i = '0' then
        s_counter <= 0;
        s_clk_out <= '0';
      elsif rising_edge(clk_i) then
        if s_counter >= unsigned(ticks_i) then -- >= because the value can change
          s_counter <= 0;
          s_clk_out <= not s_clk_out;
        else
          s_counter <= s_counter + 1;
        end if;
        clk_o <= s_clk_out;
      end if;
    end process;
    
    end Behavioral;

    • Cancel
    • Vote Up +2 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • Jan Cumps
    Jan Cumps over 3 years ago

    While I'm building up Shabaz' SDR, I'm refining the FPGA quadrature oscillator.

    I'd like to make its frequency change in steps. Programmable from the Zynq ARM/Linux PS.

     

    To prevent that I have to alter the quadrature oscillator VHDL, I'm adding a clock changer at the input of its clock input.

    At this moment a naive one. A clock divider.

     

    image

     

    The output frequency of the quadrature oscillator is always its input frequency / 8.

    So if I divide the input clock by 2, the effective frequency is input / 16.

    • Cancel
    • Vote Up +2 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • jc2048
    jc2048 over 3 years ago in reply to shabaz

    impressive to hear that the FPGA clock capabilities can at least resolve down to 60 kHz ballpark

     

    Yes, but it's not neat and tidy like it would be if the PD was down at the channel spacing value and the outputs were all simple multiples of that, like a frequency synthesizer would do it.

     

    Here I was looking at varying the divider down from the input clock to the PD, the divider down from the VCO output to the PD, and the divider down from the VCO for the output. It's all sorts of odd values coming out. Here it is as a partial table.

     

    image

     

    The 48MHz I've divided by 8 to give 6MHz [above the PD min], then the VCO is about 200 times that, so 1.2GHz [less than the max of 1300MHz], and an output divide of around 100 to place the resulting frequency near 12MHz [an arbitrary choice of tuning frequency]. The rounded figures show a rough 60k granularity to the result.

     

    To be fair to Altera/Intel, they haven't designed the PLL to do this and be used in a general way for telecoms and clock recovery.

     

    I guess external loop filter+VCO could be used

    According to the datasheet, a 74LV4046 can get to 38MHz. [Aside: the 74LV4066 is a good bit faster than the HC one, too]. That isn't enough for 4x HF band, but I wondered if one could then simply perch the FPGA PLL on top to generate the 4 phases [or, alternatively, multiply it by 4 and use one of these logic schemes].

    • Cancel
    • Vote Up +3 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • Jan Cumps
    Jan Cumps over 3 years ago in reply to shabaz

    With the vhdl code version, you can go from Hz to MHz.

    • Cancel
    • Vote Up +1 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • shabaz
    shabaz over 3 years ago in reply to jc2048

    I see.. I guess external loop filter+VCO ould be used, but then that's becoming a major project in itself. Still, impressive to hear that the FPGA clock capabilities can at least resolve down to 60 kHz ballpark, that's useful to know for other projects too, and where the ADC sample rate allows for more than that bandwidth.

    • Cancel
    • Vote Up +1 Vote Down
    • Sign in to reply
    • More
    • Cancel
>
element14 Community

element14 is the first online community specifically for engineers. Connect with your peers and get expert answers to your questions.

  • Members
  • Learn
  • Technologies
  • Challenges & Projects
  • Products
  • Store
  • About Us
  • Feedback & Support
  • FAQs
  • Terms of Use
  • Privacy Policy
  • Legal and Copyright Notices
  • Sitemap
  • Cookies

An Avnet Company © 2025 Premier Farnell Limited. All Rights Reserved.

Premier Farnell Ltd, registered in England and Wales (no 00876412), registered office: Farnell House, Forge Lane, Leeds LS12 2NE.

ICP 备案号 10220084.

Follow element14

  • X
  • Facebook
  • linkedin
  • YouTube