element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • Members
    Members
    • Benefits of Membership
    • Achievement Levels
    • Members Area
    • Personal Blogs
    • Feedback and Support
    • What's New on element14
  • Learn
    Learn
    • Learning Center
    • eBooks
    • 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
    • Project14
    • Arduino Projects
    • Raspberry Pi Projects
    • Project Groups
  • Products
    Products
    • Arduino
    • Dev Tools
    • Manufacturers
    • Raspberry Pi
    • RoadTests & Reviews
    • Avnet Boards Community
    • Product Groups
  • 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
FPGA
  • Technologies
  • More
FPGA
Blog Learning Xilinx Zynq: port a Spartan 6 PWM example to Pynq
  • Blog
  • Forum
  • Documents
  • Events
  • Polls
  • Files
  • Members
  • Mentions
  • Sub-Groups
  • Tags
  • More
  • Cancel
  • New
FPGA requires membership for participation - click to join
Blog Post Actions
  • Subscribe by email
  • More
  • Cancel
  • Share
  • Subscribe by email
  • More
  • Cancel
Group Actions
  • Group RSS
  • More
  • Cancel
Engagement
  • Author Author: Jan Cumps
  • Date Created: 7 Jun 2021 6:58 PM Date Created
  • Views 3830 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 1 year 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 1 year 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 1 year 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:
  • Jan Cumps
    Jan Cumps over 1 year ago in reply to Jan Cumps

    I started that exercise to make the GaN fet controller.

    Meanwhile, I'm learning to make hierarchical diagrams.

    image

     

     

    image

    image

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

    If I use 6 bits for the counter, with the 100 MHz FPGA clock that I'm using:

    • it would give me 64 steps. Good enough for smooth transition between 0 and 100%
    • the frequency would be 100 MHz / 64 = 1.5 MHz
    • the deadband resolution would still be one clock tick (resolution of the dead time is depending on the clock frequency, not on the duty cycle bit size) , 1/100 MHz = 10 ns

     

    That would be an ideal signal to control GaN FETs. They like to be in the +- 1 MHz range, and the LMG5200 half bridge I have needs 9 ns of deadband.

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • cstanton
    cstanton over 1 year ago

    This inspires me to spend more time with an oscilloscope...

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

    Looks like beacon_dave has done this exercise before: PYNQ-Z2 Workshop - AXI GPIO

    I'm going to see if it 'll help me create the AXI interface for the PWM design here ....

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

    That's one super annoying issue with Xilinx, you need a specific Vivado version to build stuff. I recently downloaded 2020.3, and its crazy, it only supports Versal! So I had to reinstall 2020.2 to be able to use it with my Spartan-7 and Zynq boards. Apparently Xilinx wants us to have dozens of Vivado versions, and Linux images installed in our computer.

    • 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 © 2023 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

  • Facebook
  • Twitter
  • linkedin
  • YouTube