PYNQ-Z2 SPI clock frequency

Hi all,
I am using PYNQ-Z2 board as an SPI slave. In Vivado design, I found that in ZYNQ7 Processing System, the actual frequency of SPI clock is 10 MHz, while 166.666666 MHz is requested (as shown in the figure below).

What causes the actual frequency very much lower than the requested?
Also, is it possible to make the actual frequency higher?

Thanks!

1 Like

This is related to the way Vivado works. You can define these PS settings in Vivado, but they get applied during boot so they need to be part of the boot image.

This means that when using PYNQ, PS settings are pre-defined and are part of the PYNQ image. This is why you don’t see the clock you applied.
You would see something similar happen for PS-PL clocks but as these clocks are much more important for your FPGA design, PYNQ will parse the HWH (which includes information on PS-PL clocks. When you download an Overlay, the clocks automatically get updated. You can also manually change PS-PL clocks with the pynq.ps.Clocks class.
As far as I’m aware, the Clocks class doesn’t support the SPI clock, and we don’t have similar capability.
You may be able to check the TRM to find a register for this clock and manually update it (using PYNQ Register class).
Failing that, I think you would need to rebuild your PYNQ image with the clock you need.

Cathal

4 Likes

Hi Cathal,
Thanks for the suggestions. I have found some information, but I’m not sure how they could help me.

First, I follow your suggestion using “pynq.ps.Clocks” and gets the following clock frequencies.
image
Then, I looked up “ip_dict” and try to find information about the frequency and clock.
image


As shown in the above figures, it seems like the SPI IP is using the FCLK0, which agrees to my block design.

However, in the first and third figures, the two frequencies don’t add up, which confuses me more…
Does this mean FCLK0 is designed to be 100MHz, but the actual frequency is 62.5MHz?
Does this have anything to do with the actual SPI frequency?

By the way, I have gone through AMD Adaptive Computing Documentation Portal (is this the TRM you mentioned?) and didn’t find much about the SPI frequency.

You are mixing up two different SPIs. The Zynq Processing System has a hard SPI block that you can use. The clock for this is configured from the IO Peripheral Clocks (you shared a screenshot for this in your first post).

The AXI SPI that you implemented in the PL (axi_quad_spi) is separate to the PS SPI. This will appear in the ip_dict. This SPI clock will be whatever FCLK0 is.

However, in the first and third figures, the two frequencies don’t add up, which confuses me more…
Does this mean FCLK0 is designed to be 100MHz, but the actual frequency is 62.5MHz?
Does this have anything to do with the actual SPI frequency?

I’m not sure what is happening here. Did you change the FCLK0? If you didn’t, then this may be a bug. I would ignore the ip_dict value, and refer to the FCLK0 value for this IP.

Cathal

1 Like

@cathalmccabe
@Henk_Chang

Did you see detail on the first image of the SPI clock?
The user not even enable SPI in the ZYNQ either SPI0 or SPI1 so very sure the frequency is not modifiable. There are no cross left to the 166.6666

Second you should guide the user to use EMIO from first place if IO is not mapped to MIO.

ENJOY~

Thanks for making things clear! And I have some questions about the two SPIs.

  1. What is the difference between the PL SPI and PS SPI? Which is more prefered?
  2. How the PS SPI is set up and implememted? I have only used the SPI IP before.

Also, about the clock frequencies I get from “pynq.ps.Clocks”, I do have changed the FCLK0 to 100MHz, which is default at 50MHz. However, the frequencies including the CPU and FCLK0 are still wrong when I change it back to the defaults. The setting process is fine but it seems like the CPU frequency is limited to 650MHz somehow.
image

@Henk_Chang

  1. PL or PS that’s the question. PS SPI ← AKA ARM-based (no logic element is need to construct the function, only when routing the physical signal to EMIO could introduce minimum of LUT).
    ARM SPI is more easier to test and sanity of such SPI is 100% sure pass with Build-In-Self-Test (BIST).
    ARM SPI is just like any STM32 or AVR or PIC SPI, so any MCU knowledge can also easily apply on such method.
    When come to PL-based SPI, it is using LE so this mean any bug or design fault could also make this unstable or even non-functionable. So why bother when you have right to use hardcore SPI0 / SPI1.
    Of cause if you need QSPI feature than you can either use the QSPI Flash to mimic or AXI-QSPI IP.

You need to make sure the SPI inside the ZYNQ block is enable, otherwise SPI clock is not editable.
Btw, 166MHz SPI clock is very high and board routing will become unstable if quality is bad. This also mean 100MHz or below SPI clock is already more than enough to handle most device.

As for frequency my best guess is that this could be OSC 33MHz drift from first place or the numeric update is not catching up or this frequency is fake (device-tree manually entered number) rather than real CPU speed measurement.

Better use terminal to read

dmesg
cat /sys/devices/system/cpu/cpufreq/

ENJOY~

Thanks again for the replies, it really helps me a lot. I still have some questions.

  1. PL SPI:

When I try to change the origin frequencies, some of the frequencies seem to be unavailable or not possible, as in the following figure.

Also, I’ve tried to use the command you gave, but I didn’t get information about the frequencies, perhaps I did it wrong?

By the way, since I’m using SPI as slave, does it still matter what the SPI frequency is?
And do I need to modify the origin connections/designs for the SPI IP?

  1. PS SPI:

An error appears, as the following figure, when I try to set SPI frequency 60MHz

I use the default frequency setting (166.66666MHz; will lower it when I know how) and the block design below to create a bitstream successfully.

And I’m stuck now, I don’t know how to continue SPI with Jupyter!
What I used to do with PL SPI is according to the post:

I don’t think they work the same way, do they?
I’ve seen some posts using MicroBlaze or spidev, but I can’t figure out how exactly they put it into practice.

@Henk_Chang

You need to understand source clock and pre-scaled clock.
Just like when you get lunch you can use same price for a large portion.
However, you can select to take a full or half or quarter portion.

See the error message, you need to set to >11xMHz.
However this doesn’t mean it is run in such frequency.
Do you have any training on MCU like STM32 PIC or AVR?
ZYNQ ARM A9 is just an advance MCU.

ENJOY~

Please correct me if I’m wrong. I have little experience in Arduino and F28335 DSP and to my knowledge, I will need to read/write to the registers. Pre-scaled clock can also be modified through those registers.

Here comes the question: How do I read/write to the registers?

First, I try to find the IP blocks.


I think the SPI should be in the ‘processing_system7_0’ IP where my block design connects to, so I do the following and an error shows.

Then, I try to directly use MMIO, but I fail to write to the register.

What have I done wrong or missed here?

@Henk_Chang

As long as you sure the PYNQ Image had SPI driver enable aka kernel do have the necessary ,ko and bin you can learn from here:

Good example and both IIC and SPI.
ENJOY~

I installed the spidev package as the example.
However, I found no spidev in /dev, so the spi.open(0,0) does not work.
image


Does this mean I need to build/enable an SPI driver? If so, how do I do that?

@Henk_Chang

Okay, this means default PYNQ had not enable SPI device-tree defaulty.
See:

You could replace the device-tree node or use dtbo if possible.
No matter how you still need to modify a bit the Image or generate a dtbo / replace dtb.

If you feel this is too tedious than you have no choose to use AXI-SPI via MMIO or default PYNQ API.
I might be wrong but sound like PYNQ on ZYNQ7000 is not enabling spidev via MIO spi1 & spi0 but ultrascale-ZYNQ does.
This is puzzling~

ENJOY~

You’ve asked a few questions in this thread, so I’ll try break it down into all the sections. You may want to go back and check my first and second replies.

Background info; for a typical Zynq or PYNQ system you have:
Boot image

  • Configures the PS (applies clock settings, configures PS peripherals like the Ethernet, SD card controller, USB and other settings)
  • Operating system - in the case of PYNQ this is the Ubuntu based OS you see when you boot your board
  • Software applications - for PYNQ as you have an desktop OS, you can develop your software application when the system is running - using Python, or other languages)

FPGA design - can be part of the boot image, but for your own custom design, you will copy this to your board once the system is running.

These are PS settings:


When you are creating your FPGA design in Vivado you will generate a bitstream to load to the Programmable Logic (PL). You can change PS settings, but most of these settings will not be included in the bit file, so they will have no effect when you load your Overlay in PYNQ. If you built your own boot image and use it to boot your board, then these settings would be applied at boot time.
The exception to this are the PL Fabric clocks. They get stored in the HWH, and when you load an Overlay in PYNQ the PL clock will be automatically set for your bitstream (using the PYNQ Clocks class).

about the clock frequencies I get from “pynq.ps.Clocks”, I do have changed the FCLK0 to 100MHz, which is default at 50MHz. However, the frequencies including the CPU and FCLK0 are still wrong when I change it back to the defaults.

Once your board has booted and PYNQ is running, you can manually overwrite the PL clock settings before, during or after you configure the PL. This can be interesting (and fun!), but you should not really do this for a real design. When you built your bit file in Vivado it creates a design and runs static timing analysis for a specific PL clock frequency. If you load the bit file then set the clock to something else, there is no guarantee it will run correctly.
Most other PS settings are ignored when you load an Overlay in PYNQ. In paticular the PS SPI clock will not be changed.

When I try to change the origin frequencies, some of the frequencies seem to be unavailable or not possible

Not all clock frequencies are available. You have an input frequency which comes from an external chip on the board that is connected to the Zynq. You can see in your screenshots that the input clock for the PYNQ-Z2 is 33.33 MHz. Most other clocks are derived from this. There are clock multipliers and dividers to generate values you may need. The 650MHz main CPU clocks is derived from this main clock. The reason it is 650MHz is due to the combination of multipliers and dividers.
If you try to change a clock using the PYNQ clocks class, it checks the value you requested, and calculates if it can achieve this. If it can’t, it sets the clock to the nearest value it can and generates a warning.

TO understand this better, go to Vivado and try change the input clock frequency and see how this changes the other clock settings that are available to you. Check the advanced view and look for the multiplier and divider values. Vivado tries to calculate these for you based on the clock value you want.

This is a PS setting, so it will not be applied when you load your Overlay:

This is my earlier comment:

As far as I’m aware, the Clocks class doesn’t support the SPI clock, and we don’t have similar capability.
You may be able to check the TRM to find a register for this clock and manually update it (using PYNQ Register class).
Failing that, I think you would need to rebuild your PYNQ image with the clock you need.

To explain this again, most PS settings are controlled from registers. If you can’t find the Register to change this PS clock, and if you really needed it, you would need to recompile the PYNQ image using your updated PS settings.

  1. What is the difference between the PL SPI and PS SPI?
    The PS SPI is a hard SPI controller that is part of the PS. It is intended to be connected to MIO pins. It needs to be included in the Linux device tree. This would need changes to the PYNQ image, or a dynamic change to the device tree once the board is running.

Which is more prefered?

You probably DON’T want to use the PS SPI. The PS peripherals are primarily intended to be connected to MIO pins (Multiplexed IO). These are physical pins on the Zynq device. On the PYNQ-Z2, you don’t have MIO pins available that you can connect the SPI to. You can use EMIO pins (external MIO) that allow you to connect the PS peripherals into the FPGA. However, as already pointed out you need to have the SPI in the Linux device tree, so

In my opinion, the PL SPI would be easier to use, and would give you more flexibility. The downside is that it uses some PL logic. The logic required is relatively small vs the total size of PL, so it is unlikely this would be an issue for you.
Which way is best depends on what you want or need for your design.

I try to directly use MMIO, but I fail to write to the register.

The way you are using MMIO is OK, but you can’t read and write all registers. Some are read only, some are write only - so reading back will give a 0 or an error.

Your question was about the “PYNQ-Z2 SPI clock frequency” but you really just want to use a SPI.
There are some good resources for PYNQ. I’d suggest this if you want to use the PL SPI:

In the base Overlay for PYNQ-Z2 we also have SPI controllers as part of the MicroBlaze subsystems connected to the PMODS and RPi interfaces. We have a simple Python interface for controlling the SPI.
https://pynq.readthedocs.io/en/v3.0.0/pynq_libraries/pynqmb_reference.html

I’d suggest you can close this post, and open a new post if you have further questions about using the PL SPI.

Cathal

1 Like