Jump to content
  • 0

Zynq SPI0 - EMIO


engrpetero

Question

I remain puzzled at why my AXI QSPI block doesn't seem to work.  I appreciate the effort Xilinx put into developing the driver files for much of their IP blocks and in general, I understand and like the parallel structure they seem to use across them, but I think they've often made them unnecessarily complicated, and the documentation could certainly be improved.  My debugging efforts have yet to actually see data transferred on the SPI bus.

So I figured I'd take a step in a different direction and use one of the SPI blocks from the Zynq IP to see an actual working SPI implementation.

If I enable SPI 0, for example, there are a few options for the pins that can be used.  Looking at the Zybo-10 schematic, the MIO 16..21 (ENET 0), 28..33 (USB 0), and 40..45 (SD 0) pins are all connected directly to the mentioned peripherals, so they don't seem to be able to be used for my test purpose.  That leaves EMIO as the only available option.  But... I don't know how to route EMIO to output ports in the Vivado design.  If this is even possible, does anyone have a doc or example they could point me to, please?

image.thumb.png.2c44622340716a443002852a506324f8.png

Link to comment
Share on other sites

16 answers to this question

Recommended Posts

  • 0
Connecting one of the ZYNQ PS UARTs to your PL design using the EMIO is pretty straight-forward. In theory, one could use a PS GEM for bi-directional DMA of data at about 125 MiB/s. Unfortunately, for my needs the ZYNQ GEM software is too complicated to justify developing such an interface; easier to use an AXI streaming IP.

I looked at doing an SPI or GPIO EMIO interface just for fun and decided that these also were not worth the effort for anything that I could think of as functionality that I needed. That doesn't mean that you should come to the same conclusion.

I'd advise reading the ZYNQ TRM, especially the parts about the EMIO connections and pin functions as they relate to the SPI. It's not going to be a trivial exercise.

I suspect that connecting a graphics display through the PL is going to be a time consuming project for you.. unless you find hardware that comes with a Vitis hw/sw project with source code designed to be used with your board. Note that the Z7000 PS QSPI is cannot be connected to the PL through EMIO. The SDIO can be connected to the PL through the EMIO but is limited to 25 MHz. (See page 48 of the ZYNQ 7000 TRM ).

Digilent sells one bit-mapped graphics PMOD LCD display but has never tried to use with any of their FPGA boards as far as I can tell.

It's a hard lesson to learn, but ZYNQ and Xilinx tools and IP have limitations, both in terms of functionality and support that have no simple solution. This can be a problem if your start off implementing a design on a specific platform doing the easy stuff first and then want to add new functionality. Things can get very complicated very quickly.

Personally, I dislike the functionality and performance of the Z7020 PS SPI that I always opt for an HDL PL SPI. The GEM is pretty much the same. For low speed connectivity a PS UART through the EMIO does seem to be worth the effort and pretty useful as well. Edited by zygot
Link to comment
Share on other sites

  • 0

OK - so I won't go that route (for now at least, heeding your comments).  The Xilinx AXI QSPI seems like it should work.  The IP seems pretty mature (I know that seeming mature is no guarantee).  I'll keep plugging away with it.  I can't even get the standard SPI working (much less the Quad SPI) to do anything useful.  When debugging with Vitis, and monitoring with the Digital Discovery or even a voltmeter, I never even see the CS line go LOW.  Hopefully, I stumble on a solution to it soon.  

Good and bad about the communications IP I've interacted with from Xilinx is that their examples almost always have a LOOPBACK mode.  That's good because presumably that doesn't require any*other* hardware to demonstrate that something works.  Bad because it ALSO doesn't require any*other* hardware to demonstrate that something works.  I wish they included, perhaps with commented out lines, the things needed to make it work no in loopback mode.  I figure SOMEONE has spent a good bit of time writing those drivers.  They must have a very good feel for how they should work.  A little more effort on their part would make it much easier for users as well.  I've studied and stepped through the example code A LOT.  But it also took an (unreasonably) long time to update the Xilinx drivers for the UARTs to use them the way I intended.

The graphical display I've got (from Newhaven Displays) does have some example code (arduino (which I never use), FT90, and an MSVC app that includes an emulator).  It is intended for SPI comm and so it does have a reduced but still more than functional enough set of features for the way I'll be using it.  

Edited by engrpetero
edited for clarity.
Link to comment
Share on other sites

  • 0

I know you've moved off of this route, but for future reference, once a PS peripheral is configured for EMIO, it shows up as a separate interface on the Zynq block:

image.png

This can be connected to other IPs or to an external port as is normal for any interface of any block. If you just wanted to connect the interface pins to FPGA pins, you could make the interface external and add location constraints as appropriate. If you haven't done it before, see here for what "make external" looks like on another IP: https://digilent.com/reference/programmable-logic/guides/vivado-add-gpio.

Link to comment
Share on other sites

  • 0

I had not seen this on the Zynq block.  Thanks for pointing it out.  I really intended to just do a test to see if I could see ANYTHING on the Zynq SPI_0.  I'll try that anyway with a simple new project that just includes the interface pins CS, SCK, MISO, MOSI for the SPI port on the Zybo I have.  I also just realized that I'll still be using Xilinx drivers from Vitis.  Maybe they will be easier to use.  I'd love to see an SPI transmission received on the DD just so I know what to expect.  Then I can go back to the QSPI driver and continue plodding.  Thanks @artvvb!

Link to comment
Share on other sites

  • 0
Be aware that if you configure SPI1 as being connected to the PL via EMIO, the pins for SPI1 on the ZYBQ processor block don't necessarily behave the same as, say SPI0 connected to the MIO pins. Read the Z7000 TRM before trying to use the interface.

Also a SPI connected to the EMIO runs at 25 MHz max, while an SPI connected to MIO pins runs at 50 MHz max.

Lot's of details to understand.
Link to comment
Share on other sites

  • 0

Well good gracious.  I finally got something working - at least I'm seeing transmissions on the original QSPI now.  I'd almost like to offer to update the Xilinx docs! :-)

I'll trouble you gents for a few more pieces of info though, please, if you have answers or experience.

For the AXI Quad SPI (version 3.2), looking at the documentation, it seems the SPI output clock frequency is the AXI clock frequency divided by the 'Frequency Ratio'.  If I use a Frequency ration of 16, it appears the output clock pulses happen about every .332 us which would correspond to an undivided frequency of 50MHz.  From my review, it *seems* the AXI clock frequency is this one?image.thumb.png.35cc3c8f844af40952a5ef94eb623e5a.png

Link to comment
Share on other sites

  • 0

I do not think so. FCLK_CLKx are the output clocks from Zynq for use in PL.

The SPI frequency in your case is set to default 166.67 MHz, as seen in IO Peripheral Clocks.

If you set in your app the ratio by this call:

XSpiPs_SetClkPrescaler(&SpiInstance, XSPIPS_CLK_PRESCALE_16);

The SPI CLK frequency will be 166.67 / 16 = 10.42 MHz.

Viktor

Link to comment
Share on other sites

  • 0
19 hours ago, engrpetero said:

I'll try that anyway with a simple new project that just includes the interface pins CS, SCK, MISO, MOSI for the SPI port on the Zybo I have.

I can offer you two sample projects of mine for inspiration.

The first one is a demo of using 3.5″ TFT SPI Module ILI9488 with Zybo Z7. The display is connected to PS via PS SPI.
I guess the PS SPI initialization sequence may be interesting for you; see here.

The second one showcases the same display, but this time, it's controlled via AXI SPI by a code running in PS.

Both sample projects include a HW design in Vivado 2023.1 and a workspace for Vitis 2023.1

The projects are part of the repository where I publish my port of Arduino ILI9488 library to Xilinx SoC and FPGA. I need to finish some readme files, but the library and examples are ready and functional.

Viktor

Edited by Viktor Nikolov
Link to comment
Share on other sites

  • 0
26 minutes ago, Viktor Nikolov said:

I do not think so. FCLK_CLKx are the output clocks from Zynq for use in PL.

The SPI frequency in your case is set to default 166.67 MHz, as seen in IO Peripheral Clocks.

If you set in your app the ratio by this call:

XSpiPs_SetClkPrescaler(&SpiInstance, XSPIPS_CLK_PRESCALE_16);

The SPI CLK frequency will be 166.67 / 16 = 10.42 MHz.

Viktor

Interesting, @Viktor Nikolov.  I changed the value in the Vivado IP dialog (first pic) from 16 (my successful test above) to 8 and the clock frequency I measure with the DD (essentially a scope) is twice as fast and still results in a undivided clock frequency of 50MHz (second pic).  I'm currently only using a slightly modified version of the xspi_polled_example project so there is (at least no obvious) modifying of any clock.   

On the vivado side, I've just 'run automation' to connect the zynq to all axi peripherals.

image.thumb.png.ce108c39ce1c56f4f45f07a2fc74cae7.png

image.png.41f7edbc1b723aff6a276c80abd399b9.png

Edited by engrpetero
fixed 'period' to 'frequency'
Link to comment
Share on other sites

  • 0
12 minutes ago, Viktor Nikolov said:

I can offer you two sample projects of mine for inspiration.

The first one is a demo of using 3.5″ TFT SPI Module ILI9488 with Zybo Z7. The display is connected to PS via PS SPI.

The second one showcases the same display, but this time, it's controlled via AXI SPI by a code running in PS.

Both sample projects include a HW design in Vivado 2023.1 and a workspace for Vitis 2023.1

The projects are part of the repository where I publish my port of Arduino ILI9488 library to Xilinx SoC and FPGA. I need to finish some readme files, but the library and examples are ready and functional.

Viktor

Thank you very much for sharing.  I'll certainly take a look!

Link to comment
Share on other sites

  • 0
44 minutes ago, engrpetero said:

For the AXI Quad SPI (version 3.2), looking at the documentation, it seems the SPI output clock frequency is the AXI clock frequency divided by the 'Frequency Ratio'.  If I use a Frequency ration of 16, it appears the output clock pulses happen about every .332 us which would correspond to an undivided frequency of 50MHz.  From my review, it *seems* the AXI clock frequency is this one?

The AXI Quad SPI has an "ext_spi_clk" port, which is divided by the frequency ratio (which must also be at least 2) to derive SCLK. You probably have this pin connected to the AXI clock. See the "Frequency Ratio" bullet here: https://docs.xilinx.com/r/en-US/pg153-axi-quad-spi/SPI-Options.

Link to comment
Share on other sites

  • 0
45 minutes ago, engrpetero said:

I changed the value in the Vivado IP dialog (first pic) from 16 (my successful test above) to 8 and the clock frequency I measure with the DD (essentially a scope) is twice as fast and still results in a undivided clock frequency of 50MHz (second pic). 

Sorry, my bad. I thought that you were using PS SPI, but you are using an AXI Quad SPI from PS. 🙂

@artvvb is of course right. The ratio is for the ext_spi_clk.

In the ILI9488 AXI SPI sample project, I needed to have a 20 MHz SPI clock for the display. Therefore, I feed AXI SPI IP ext_spi_clk input by 40 MHz from the clocking wizard. The Frequency Ratio on the AXI SPI IP is set to 2.
Rest is clocked by 100 MHz from the FCLK_CLK0 output of Zynq.

Viktor

Zynq_PS-GPIO_AXI-SPI_diagram.png

Edited by Viktor Nikolov
Link to comment
Share on other sites

  • 0

Yes, my real question was about the source for the initial 50MHz clock.  The Frequency ratio divisor of 2, 8, 16 shows that it works exactly as the IP documentation suggests and uses a 50MHz clock as its source.  I was curious the 50MHz clock hence my pic of the Zynq clocks posted above.  The block diagram - which I should have looked at earlier - makes this abundantly clear.

image.png.44a4722db79f70b02b2bd55e16ed1abd.png

As usual, the solution to this problem turned out to be shockingly simple.  But the driver docs could certainly be updated to make it easier for people to figure out in the future.  This took a little longer as well due to my learning how to use the Digital Discovery to look at SPI signals.  I am very happy with all the Digilent products I have and am very thankful to the Digilent folks who help out on the message board.  However, I think the helpful DD/Waveforms documentation could be augmented with some examples (or perhaps I've just missed them).

 

Edited by engrpetero
Link to comment
Share on other sites

  • 0

For anyone that makes it to the bottom of this thread, when using the xspi_polled_example example program for the V3.2 driver of the V4.9 AXI QSPI peripheral, to run this example with an actual slave on the first slave bit, simply make the two changes enumerated below.  It is of course a very good idea to step through the XSpi_Transfer() function to clearly understand what it is doing (which will also allow modifying it to be more useful to a particular application).

  1. Remove the Loopback mode bit mask from the XSpi_SetOptions() function (this is pretty obvious as we just want to use normal mode).
  2. The line SpiInstancePtr->SlaveSelectReg = 0x02;.  My AXI QSPI has two slaves so the SlaveSelectMask is 0x03.  The SlaveSelectReg needs to have exactly one '0' and it should be in the bit position for the slave you want to use.  The example does not have this line as it only uses loopback mode.
	/*
	 * Set the Spi device as a master and in loopback mode.
	 * Note: Remove Loopback mode for normal operation (obviously)
	 */
	Status = XSpi_SetOptions(SpiInstancePtr, XSP_MASTER_OPTION);// | XSP_LOOPBACK_OPTION);
	if (Status != XST_SUCCESS) {
		return XST_FAILURE;
	}

	/*
	 * Start the SPI driver so that the device is enabled.
	 */
	XSpi_Start(SpiInstancePtr);

	/*
	 * For the slave to make active, set the SlaveSelectReg to have a value
	 * where the bit corresponding to the desired slave is a '0' and all 
	 * other bits are '1'.
	 */
	SpiInstancePtr->SlaveSelectReg = 0x02; // ZZZ

	/*
	 * Disable Global interrupt to use polled mode operation
	 */
	XSpi_IntrGlobalDisable(SpiInstancePtr);

	/*
	 * Initialize the write buffer with pattern to write, initialize the
	 * read buffer to zero so it can be verified after the read, the
	 * Test value that is added to the unique value allows the value to be
	 * changed in a debug environment.
	 */
	Test = 0x10;
	for (Count = 0; Count < BUFFER_SIZE; Count++) {
		WriteBuffer[Count] = (u8)(Count + Test);
		ReadBuffer[Count] = 0;
	}

	/*
	 * Transmit the data.
	 */
	XSpi_Transfer(SpiInstancePtr, WriteBuffer, ReadBuffer, BUFFER_SIZE);
		

 

Edited by engrpetero
Link to comment
Share on other sites

  • 0

I've found Xilinx driver code to be obtuse and buggy while writing my own for the UARTS, timers, and other simpler modules using the TRM is pretty easy. More than once I've had the experience of studying/debugging the provided code only to realize that I could have it working already if I'd started with the TRM and set set/clear the bits myself.

I really like MPSOCs because the combination of FPGA and PS makes it easy to create your own peripherals in the fabric specific to the part and use. For instance, if I was interfacing to an SPI display I would write Verilog IP that could do the SPI and higher level maintenance of display with an AXIlite interface so the processor can simply write chars to the local buffer and not be concerned with the rest of the process. 

Link to comment
Share on other sites

  • 0

Thanks for the reply, @Richm.  I do agree with you on the same experience studying/debugging the provided code.  The biggest benefit I've found from studying the provided code was to learn how those developers provided a (in most cases) very parallel way of doing things.  And it helped me to make appropriate modifications for my use.  

For the character display, I don't disagree with you that the this might have better been done by just addressing it with a new AXI lite IP design.  I'd like to still do that sometime (as a learning exercise as much as anything).  I don't think that would be as easy with the graphical display - perhaps I'm wrong there too though.  The PS does make it very easy to make changes but I do understand there are tradeoffs (the PS can get loaded up for one).  With functional SPI frequencies of up to 1MHz for the char display and 25MHz for the graphical display, the PS is sufficient for use.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...