element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • About Us
  • 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: port a Spartan 6 PWM example to Pynq
  • 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: 7 Jun 2021 6:58 PM Date Created
  • Views 7979 views
  • Likes 12 likes
  • Comments 14 comments
  • zynq
  • jupyter
  • fpga
  • vivado
  • pynq
  • spartan
Related
Recommended

Learning Xilinx Zynq: port a Spartan 6 PWM example to Pynq

Jan Cumps
Jan Cumps
7 Jun 2021

The goal of this blog series is to master the Xilinx Zynq.

I'll try to build a PWM controller for a half bridge power design.

image

I've made a PWM with dead time design for the Xilinx Spartan 6 FPGA in 2017.
I'm now learning to design for Zynq (I got a Pynq-Z2 board from our balearicdynamics!). I use
the 2017 Spartan 6 design as a starting point.

 

 

Goals for this post

 

I want to have that PWM VHDL design running on the Zynq, and be able to change the dead time and duty cycle from the ARM processors inside the Zynq.

The output signals have to be available for probing.

To set duty cycle and dead time, I'll use the simplest solution for now: the GPIO port between ARM and FPGA fabric.

 

The VHDL PWM with Dead Time

 

Very close to the version of the original post. Only edited to deal with obsolete constructs.

Latest source on github: https://gist.github.com/jancumps/36f21e89bfb8e44f3dba7bf014ffd198

 

library IEEE;
use IEEE.std_logic_1164.all;

package PulsePckg is
  constant HI   : std_logic := '1';
  constant LO   : std_logic := '0';
  constant ONE  : std_logic := '1';
end package;

library IEEE, UNISIM;
use IEEE.MATH_REAL.all;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
use UNISIM.vcomponents.all;
use WORK.PulsePckg.all;

entity Pwm is
  port (
    clk_i  : in  std_logic;             -- Input clock.
    duty_i : in  std_logic_vector (7 downto 0);      -- Duty-cycle input.
    band_i : in  std_logic_vector (3 downto 0);      -- number of clock-ticks to keep both signals low before rising edge
    pwmA_o  : out std_logic;            -- PWM output.
    pwmB_o  : out std_logic             -- PWM output inverse.
    );
end entity;

architecture arch of Pwm is
  constant MAX_DUTY_C : std_logic_vector(duty_i'range) := (duty_i'range => ONE);
  signal timer_r      : natural range 0 to 2**duty_i'length-1;
begin

  process(clk_i)
  begin
    if rising_edge(clk_i) then
       pwmA_o   <= LO;
       timer_r <= timer_r + 1;
      if timer_r >= unsigned(band_i) + unsigned(duty_i) then
pwmB_o <= HI;
end if;
      if timer_r < unsigned(duty_i) then
        pwmB_o <= LO;
        if timer_r >= unsigned(band_i)  then
pwmA_o <= HI;
  end if;
      end if;
    end if;
  end process;

end architecture;

 

It has 3 inputs:

  • the clock. It will come directly from the ARM side
  • duty cycle. Also coming from the ARM. Via 8 bits of the GPIO connector.
    If you need a faster output frequency, you'll have to compromise on the bit width. The less precision, the faster the outpu.
  • dead time. Also coming from the ARM. Via 8 bits of the GPIO connector. The number of clock ticks that both output signals should stay off, to prevent overshoot in the power half bridge you'll drive with this design.

and 2 outputs: the complementary driving signals for the half bridge, with dead time injected.

image

image: preview of what the final output will be. The cursors show the dead time before output A switches high.

 

Interaction between the VHDL PWM and the Zynq ARM realms.

 

In my original Spartan 6 post, everything was happening inside the FPGA. There was a rotary encoder setting the duty cycle, and the PWM module reacting on that.

In my Zynq design here, there is no rotary encoder. The settings come from the Arm, running Linux.

The interface in this blog is the 64 bit GPIO pass-through.

image

image: the GPIO bus between ARM and FPGA is enabled

 

The interface will give duty cycle (8 bits) and dead time (4 bits).

I'll use 2 slice modules to cut these 2 values out of the 64 bit wide GPIO output bus.

 

image

image: interconnections. in this picture, the ARM part and the slices that provide duty cycle and dead time are highlighted.

 

The table below shows how the 64 bit port is "sliced" into different sections:

 

duty cycle dead time
image image

image: the slices that cut the 8 bit duty cycle and 4 bit dead time out of the ARM 64 bit GPIO output bus.

in a next post, I'll replace this mechanism by memory mapped interaction between ARM and FPGA fabric.

 

The duty cycle and dead time are inputs for the PWM VHDL module. The 3rd input is Zynq's clock signal.

 

This is a good time to verify the design and run a synthesis.

If successful, the output pins can be assigned. I'm going to route them to two pins on the PMODA connector:

 

Output Pins: Constraints

 

The output of the PWM block is routed to two pins on the PMODA connector:

 

image

image source: online published schematics

image

image: tapping the signals

 

 

The template XDC file for the Pynq-ZQ shows what balls on the Zynq package they relate to:

 

##PmodA
#set_property -dict { PACKAGE_PIN Y18   IOSTANDARD LVCMOS33 } [get_ports { ja[0] }]; #IO_L17P_T2_34 Sch=ja_p[1]
#set_property -dict { PACKAGE_PIN Y19   IOSTANDARD LVCMOS33 } [get_ports { ja[1] }]; #IO_L17N_T2_34 Sch=ja_n[1]

 

I could copy over that code, but used the GUI to configure them instead:

image

Before doing that, take care to generate a wrapper for the diagram, and put that as the top module.

That takes care that the inputs and outputs defined on that diagram will be the one that are accessible and can be constrained.

 

If all is good, let Vivado generate the bit stream.

 

Deploy the Design to the Pynq-Z2

 

After generating the bitstream, there are two files that you'll need to copy over to the Zynq.

Both can be found in your project subdirectories (mine is named pwm_xess)

  • the .bit file (bitstream, I found it in pwm_xess/pwm_xess.runs/impl_1)
  • the .hwh file (description of the bitstream access points, I found it in pwm_xess/pwm_xess.gen/sources_1/bd/pwm_xess/hw_handoff)

 

Coy them over to a subdirectory on the Pynq, in the Jupyter structure.

I made a /home/xilinx/jupyter_notebooks/pwm directory and put them there.

They both have to have the same filename, to make to overlay on the Pynq work.

 

image

 

Use the Design on the Pynq-Z2

 

Now that the bitsream and wrapper are deployed, I can test from a Jupyter notebook.

The notebook is available on the network by surfing to http://pynq.

I copied my files to the pwm subdirectory of the notebooks. In my browser, I navigate to that same subfolder:

image

image: my Jupyter pwm test folder contains other designs too. I highlighted the ones for this post

 

I find the .bit and .hwh file listed.

To execute the bitstream, I generate a new Python3 notebook called pwm_xess.

 

image

imaqe: my notebook after doing all the tests

 

image

imaqe: the design in action with 50% duty cycle

 

First the code to load the bitstream:

 

from pynq import Overlay
ol=Overlay("pwm_xess.bit")

 

Then the definition of the two variables that define duty cycle and dead time:

 

from pynq import GPIO

# duty
bit_0 = GPIO(GPIO.get_gpio_pin(0), 'out')
bit_1 = GPIO(GPIO.get_gpio_pin(1), 'out')
bit_2 = GPIO(GPIO.get_gpio_pin(2), 'out')
bit_3 = GPIO(GPIO.get_gpio_pin(3), 'out')
bit_4 = GPIO(GPIO.get_gpio_pin(4), 'out')
bit_5 = GPIO(GPIO.get_gpio_pin(5), 'out')
bit_6 = GPIO(GPIO.get_gpio_pin(6), 'out')
bit_7 = GPIO(GPIO.get_gpio_pin(7), 'out')

# dead time
bit_8 = GPIO(GPIO.get_gpio_pin(8), 'out')
bit_9 = GPIO(GPIO.get_gpio_pin(9), 'out')
bit_10 = GPIO(GPIO.get_gpio_pin(10), 'out')
bit_11 = GPIO(GPIO.get_gpio_pin(11), 'out')

 

All is ready now, to play with the inputs.

 

Setting the duty cycle to 50%:

 

# 50%
bit_0.write(1)
bit_1.write(1)
bit_2.write(1)
bit_3.write(1)
bit_4.write(1)
bit_5.write(1)
bit_6.write(1)
bit_7.write(0)

 

Setting a dead time of 8 clock ticks:

 

# dead time 8 clock ticks
bit_8.write(0)
bit_9.write(0)
bit_10.write(0)
bit_11.write(1)

 

Changing the value has real time impact on the generated signal

image

image: PWMA and PWMB, with generated deadband

 

In essence, I now have a half bridge controller that can be controlled from Linux.

A simple starting point, but a real one.

 

image

image: the complimentary signals are available on PMODA, pins 1 and 2

 

The Vivado project, with generated .bit and .hwh files is attached.

 

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
  • Sign in to reply

Top Comments

  • Jan Cumps
    Jan Cumps over 4 years ago +2
    Some animation: 50% duty, then looping through 0, 8 and 15 cycles of dead band from time import sleep bit_0.write(1) bit_1.write(1) bit_2.write(1) bit_3.write(1) bit_4.write(1) bit_5.write(1) bit_6.write…
  • Andrew J
    Andrew J over 4 years ago +2
    Very interesting Jan. FPGA is something I want to start looking at by the end of the year. Once I can get house renovation works out of the way!
  • Jan Cumps
    Jan Cumps over 4 years ago in reply to Jan Cumps +2
    I found a decent tutorial - need to adapt it to the latest 2020.2, but that is not hard: www.youtube.com/watch I'm following along with the video:
Parents
  • neuromodulator
    neuromodulator over 4 years ago

    Interesting!  Some time ago I was looking for a way to transfer data from PS to the PL, and it looked to me like AXI was the way to do that. Didn't know you could also do it through GPIO. How fast can you switch these GPIOs?

    • Cancel
    • Vote Up +1 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • Jan Cumps
    Jan Cumps over 4 years ago in reply to neuromodulator

    AXI is the way to go. GPIO is intended for external signals.

    I'm abusing it here because I haven't learned AXI yet. Do you have a good example for me to learn from?

     

    I don't know about the speed. I'm only going to read the documents once I understand the mechanisms - I need some context or I fall asleep

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
Comment
  • Jan Cumps
    Jan Cumps over 4 years ago in reply to neuromodulator

    AXI is the way to go. GPIO is intended for external signals.

    I'm abusing it here because I haven't learned AXI yet. Do you have a good example for me to learn from?

     

    I don't know about the speed. I'm only going to read the documents once I understand the mechanisms - I need some context or I fall asleep

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
Children
  • neuromodulator
    neuromodulator over 4 years ago in reply to Jan Cumps

    Learning how to communicate through AXI is on my TODO list, but right  now other projects have taken priority over that. I read a bit about it from one of Xilinx's PDFs, and its not trivial at all, moreover, there  are multiple AXI interfaces that are useful for different cases. One very nice Verilog site that has some articles about AXI is this one: The ZipCPU by Gisselquist Technology

    It has a few articles about AXI, so it may be a good start

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • Jan Cumps
    Jan Cumps over 4 years ago in reply to Jan Cumps

    I found a decent tutorial - need to adapt it to the latest 2020.2, but that is not hard:

    You don't have permission to edit metadata of this video.
    Edit media
    x
    image
    Upload Preview
    image

     

    I'm following along with the video:

    image

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

    OK, that isn't hard (at least the basics image ).

    image

     

    It's not yet executing my own VHDL yet, but code generated from C. But I understand how to exchange variables between the two realms with AXI now.

     

    With driver:

    image

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

    Great, I was lately trying to install an updated version of petalinux on my minized, but found out that its so hard to generate the image. For starters Xilinx does not  support just any version of linux, and the one supported by them was like 2 years old,  and tended  to autoupdate automatically to a version that was not supported. Then there are lots of things that have to be set right in the environment before it can compile the image. Its  a huge mess...

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • Jan Cumps
    Jan Cumps over 4 years ago in reply to neuromodulator

    I didn't have to build an image. 2.6 is available as SDcard image for the board I'm using.

    That together with Vivado 2020.1 should be the best combination.

     

    (unfortunately I installed 2020.2 before I knew that. It does not matter for new bitstreams. But I can't re-generate the base and logictools Vivado source projects now, and I'd like to learn from their design)

    • Cancel
    • Vote Up 0 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