Jump to content
  • 0

Arty S7-50 SPI Help


baywil

Question

Pardon the hairball of info here but, I've been banging my head for 2 weeks now so maybe I can't see the forest for the trees...my problem is that I can't see MOSI data on the 6-pin J7 connector (Arduino/chipKIT Shield connector).

I have a Rev E. board and added a Quad-SPI IP in Vivado 2023.  The QSPI s_axi_aclk and ext_spi_clk lines are connected to clk_out1 of the Clocking Wizzard at 100Mhz (via auto-connect). The QSPI's Frequency Ratio is set to 4:1 so, I think that means the QSPI is running at 25Mhz? I read the IP has a limit of 50Mhz.

I obtained Arty-S7-50-Master.xdc from Digilent's GitHub repo and in Vivado added it to Constraints in the block design. The only mods I made to it was to uncomment and modify the following:

set_property -dict { PACKAGE_PIN H16   IOSTANDARD LVCMOS33 } [get_ports { spi_ss_io  }]; #IO_L22P_T3_A17_15   Sch=ck_io10_ss
set_property -dict { PACKAGE_PIN H17   IOSTANDARD LVCMOS33 } [get_ports { spi_io0_io }]; #IO_L22N_T3_A16_15   Sch=ck_io11_mosi
set_property -dict { PACKAGE_PIN K14   IOSTANDARD LVCMOS33 } [get_ports { spi_io1_io }]; #IO_L23P_T3_FOE_B_15 Sch=ck_io12_miso
set_property -dict { PACKAGE_PIN G16   IOSTANDARD LVCMOS33 } [get_ports { spi_sck_io }]; #IO_L14P_T2_SRCC_15  Sch=ck_io13_sck

I generated a test_bench.vhd for the block design using Xilinx TCL Store Design Utilities https://support.xilinx.com/s/article/64983?language=en_US . I was able to modify the generated testbench and toggle the QSPI's spi_io0_o (MOSI ) via the test bench in simulation, viewing it in PulseView using Saleae Logic analyzer. So, I thought I was good. I created a Vivado-maintained HDL wrapper, generated a bitstream, and exported the wrapper.

In Vitis I imported the hardware wrapper and tried both xspi_low_level_example.c and xspi_intr_example.c from Xilinx/Vitis/2023.1/data/embeddedsw/XilinxProcessorIPLib/drivers/spi_v4_10/examples. Both used loopback so, I removed that flag before running them. Both perform a XSpi_SelfTest() which was successful. When I run the examples, I can watch the SPI control and status registers update so, I know I'm talking to the hardware.

The closest I came to seeing MOSI data was (in desperation) creating a loop to write hundreds of bytes to the TX data register and I was finally able to see MOSI data in the analyzer although it wasn't the same bytes written to TX so, a problem for sure.

This was the short version of my failures. I can attach pictures of settings and provide further details upon request. I'm hoping that a SPI guru out there can push me in the right direction as I believe I've missed something basic.

Link to comment
Share on other sites

9 answers to this question

Recommended Posts

  • 0

Hey @baywil,

QSPI settings would potentially be helpful, either as screenshots of the configuration wizard or copied from the Block Properties pane's Properties tab (everything in the Config dropdown).

Xilinx doesn't specify Fmax for ext_spi_clk for Spartan 7 in the AXI QSPI user guide (https://docs.xilinx.com/r/en-US/pg153-axi-quad-spi/Performance), but you may need to pass it a 50 MHz or slower clock instead, which can further be divided down. 25 MHz may also be too fast for the chipkit header pins. Common settings we've had work for Pmod connectors would be a 50 MHz ext_spi_clk with an x16 frequency ratio (3.125 MHz).

Thanks,

Arthur

Link to comment
Share on other sites

  • 0

Thanks, Arthur! I've attached screenshots of what I describe below.

In Vivado, I changed the QSPI ext_spi_clk (ext_spi_clk.png) to 50Mhz via a new clk output on the clock wizard (clk_wiz.png). I set the QSPI frequency ratio to 16:1 as you suggested (qspi_cfg.png).

In Vitis, I tried inhibiting the master while populating the TX FIFO then enabling the master (loop_code.png). No MOSI output but the TX occupancy logging showed that the 

TX bytes were added (output.png). They just never get sent out.

Alternately, if I just hammer the TX with data while master is enabled,  **EVENTUALLY** I see some MOSI data out but, it's not what was written to DTR (0us_delay.png).

(filenames are not posted but images appear in the order I listed)

 

 

 

 

 

 

ext_spi_clk.thumb.png.fb548505882fc1b5fadc3741843e8ba9.pngclk_wiz.thumb.png.5166c0c0a4f6620d74495e3febd1257d.pngqspi_cfg.thumb.png.5becd2c7048b0a341b6956c91985faef.pngloop_code.png.3ece41c79b47e3044f2c0abef6ee7707.pngoutput.thumb.png.d4096346969e3a2652555d4e53f3bcf5.pngPulseViewSPIsettings.thumb.png.83baa6658ed405101184820d9f0aeadf.png0us_delay.thumb.png.396aa666a1fa7da3a3c63389f3444193.png

Edited by baywil
Link to comment
Share on other sites

  • 0

I simplified the TX loop to send just one byte hundreds of times and printed out the registers with SPI enabled and disabled. The Transmit FIFO Occupancy register increases when SPI System Enable is disabled, otherwise it does not but, no MOSI data observed. I hope this sheds more light...

 

loop_code_single_TX_byte.png.b17bdebd6bcc10c8536deaa9f6a18500.png

SPI_disabled_TX_full.thumb.png.be3674d822f054a89f52ba3716caed25.png

 

 

SPI_enabled_TX_empty.thumb.png.20bdfde4a8cd2780bfae2bd6782507e2.png

Link to comment
Share on other sites

  • 0

I'm not too familiar with the lowlevel polled example, which I assume is what you are basing this on. Stripping down the (not low-level) polled example (adding the SetSlaveSelect call), gets something like the following, which is working fine on my system:

#include "xparameters.h"
#include "xspi.h"

void main() {
	XSpi spi;
	XSpi_Config *cfgptr;
	u8 buffer[4] = {0xde, 0xad, 0xbe, 0xef};
	u32 status;

	cfgptr = XSpi_LookupConfig(XPAR_AXI_QUAD_SPI_0_DEVICE_ID);
	XSpi_CfgInitialize(&spi, cfgptr, cfgptr->BaseAddress);
	XSpi_SetOptions(&spi, XSP_MASTER_OPTION);
	XSpi_Start(&spi);
	XSpi_IntrGlobalDisable(&spi);

	XSpi_SetSlaveSelect(&spi, 1);

	while (1) {
		status = XSpi_Transfer(&spi, &buffer, NULL, 4);
	}
}

Thanks,

Arthur

Link to comment
Share on other sites

  • 0

"device" is an instance of XSpi - I just didn't include the declaration.

Your example worked out of the box with a few exceptions. I've included modified versions for both 4 and 1 byte XSpi_Transfer().

1. MOSI data out didn't match what was in buffer[4] and every run provided completely different MOSI data.This caused me to second guessed my constraints file so, I tested two different port assignments:

set_property -dict { PACKAGE_PIN H16   IOSTANDARD LVCMOS33 } [get_ports { spi_ss_io  }]; #IO_L22P_T3_A17_15   Sch=ck_io10_ss
set_property -dict { PACKAGE_PIN H17   IOSTANDARD LVCMOS33 } [get_ports { spi_io0_o }]; #IO_L22N_T3_A16_15   Sch=ck_io11_mosi
set_property -dict { PACKAGE_PIN K14   IOSTANDARD LVCMOS33 } [get_ports { spi_io0_i }]; #IO_L23P_T3_FOE_B_15 Sch=ck_io12_miso
set_property -dict { PACKAGE_PIN G16   IOSTANDARD LVCMOS33 } [get_ports { spi_sck_io }]; #IO_L14P_T2_SRCC_15  Sch=ck_io13_sck

and

set_property -dict { PACKAGE_PIN H16   IOSTANDARD LVCMOS33 } [get_ports { spi_ss_io  }]; #IO_L22P_T3_A17_15   Sch=ck_io10_ss
set_property -dict { PACKAGE_PIN H17   IOSTANDARD LVCMOS33 } [get_ports { spi_io0_io }]; #IO_L22N_T3_A16_15   Sch=ck_io11_mosi
set_property -dict { PACKAGE_PIN K14   IOSTANDARD LVCMOS33 } [get_ports { spi_io1_io }]; #IO_L23P_T3_FOE_B_15 Sch=ck_io12_miso
set_property -dict { PACKAGE_PIN G16   IOSTANDARD LVCMOS33 } [get_ports { spi_sck_io }]; #IO_L14P_T2_SRCC_15  Sch=ck_io13_sck

These changes had no impact as you'll see in the attached images.

2. It appears that at least 300 bytes have to be written via XSpi_Transfer() before any MOSI data is observed. I tried the entire buffer[4] 75 times as well as buffer[0] (0xDE) 300 times - any less than those attempts yields no MOSI output. I am open to the possibility that this is a PulseView/Saleae analyzer issue as well not displaying the correct SPI decoder data except that the MOSI data is on the D4 line of the analyzer.

Do you have a device connected to your MOSI output (I don't)? Does your MOSI data match what is in buffer[4]?

 

arthur_code_modified_TX_4byte.thumb.png.d55861351dc297da7ff367675b12453b.png

 

arthur_code_modified_TX_1byte.thumb.png.3e6e762a9291eb4964ad06530823366a.png

 

arthur_with_io0_o.thumb.png.ed5e206a56c1ef18e44312d6507bc811.pngarthur_with_io0_io.thumb.png.fcafe6fbbab8c3f398d80324309adf60.pngarthur_with_296_bytes.thumb.png.18730f742427c5b4612e2ed830f8854b.pngarthur_with_300_bytes.thumb.png.513f0756d2f88f87c6b52de4188038bd.pngarthur_with_300_bytes_2.thumb.png.f89f84b9dec56ed40e9be578cde82df8.pngarthur_with_300_0xDE_bytes.thumb.png.50cfdd62b9a978862c028c03187e0566.png

Edited by baywil
Link to comment
Share on other sites

  • 0

Yes, I have an AD3 hooked up to some Pmod pins with a different FPGA board - there aren't any fundamental differences between 7-series boards that would affect the IP in this case. This is what a single-shot capture looks like, using the code I sent previously, triggering on protocol start (chip select falling edge). Works on the first pass, using the Vitis debugger to step through the code and only perform one transfer.

image.png

I don't have personal experience with Saleae devices, so I'm not certain how it is decoding the data, or how it triggers, but make sure that the SPI mode it uses to decode the protocol matches your expectations. Mode is configured in AXI QSPI by setting the XSP_CLK_ACTIVE_LOW_OPTION and XSP_CLK_PHASE_1_OPTION bits via XSpi_SetOptions. By default, clock is active high, and the slave should sample on the rising edge, so SPI mode 0.

Link to comment
Share on other sites

  • 0

Thank you, Arthur! Your analyzer image made me realize I was sampling at 20Khz (visible in one of the earlier images) while I had set the SPI out clock to 50Mhz/16.

I was able to set the data width of the SPI IP to 32 bits and sample the data at 8Mhz. I added the XSP_CLK_ACTIVE_LOW_OPTION flag (I completely missed this) and set Saleae accordingly and it worked.

A couple things I am unclear on:

1. In the capture below, the bytes are sent out in reverse order they are stored in buffer. I had to reverse buffer to get DEADBEEF in the last image. It looks like

XSpi_Transfer() does a XSpi_WriteReg() to XSP_DTR_OFFSET in buffer's native order. I guess this means DTR sends the data to FIFO MSB?

2. In the last image, I captured a DE8DBEEF - obviously some bits fell out? This could be my analyzer or not using interrupts? Just curious.

Thanks again for all your help!

Bill

 

arthur_code_fixed.png.c4e1949c0cc7205c488da464577199fe.png

4byte_8Mhz_sample.png.f8c602f9e22174ee4c04154348266b9c.png

 

DEAD_BEEF_8Mhz_sample.thumb.png.18e0eaf655979856e50d4d36fdef7531.png

Link to comment
Share on other sites

  • 0

Hey Bill,

Quote

1. In the capture below, the bytes are sent out in reverse order they are stored in buffer. I had to reverse buffer to get DEADBEEF in the last image. It looks like

XSpi_Transfer() does a XSpi_WriteReg() to XSP_DTR_OFFSET in buffer's native order. I guess this means DTR sends the data to FIFO MSB?

Zynq-7000 is little endian, so the first byte in memory is treated as the least in a u32. The best way to fix it would just be to declare the data to be transferred as the same width you are using in the QSPI, so "u32 buffer[1] = {0xDEADBEEF};". Note that you can see the same effect happen if you print the u8 buffer after casting to a u32 pointer: "xil_printf("status: %08x\r\n", *(u32*)buffer);".

Quote

2. In the last image, I captured a DE8DBEEF - obviously some bits fell out? This could be my analyzer or not using interrupts? Just curious.

Best guess, an 8 MHz sampling rate is insufficient. Run the analyzer faster, and potentially at an integer multiple of the SCLK frequency. At least 4x or ideally 10x the signal's 3.125 MHz.

Thanks,

Arthur

(Edited an incorrect code snippet)

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...