Jump to content
  • 0

Multiple Displays


engrpetero

Question

I am modifying one of my projects to be able to handle two different types of displays (a character display and a graphical display).  After doing some research, I've decided on two acceptable displays that both use an SPI interface.  The character display uses a simple legacy SPI interface and the graphical display can use a Quad SPI interface.  I've spent some time the last few days getting familiar with the AXI Quad SPI IP block and implementing it into my project.  It certainly seems useful and already pretty mature and can obviously handle both SPI interface types I need.  This is a baremetal app from a Vivado/Vitis perspective.

On to my question...  Interacting with the Quad SPI IP block is pretty straightforward.  However, the displays expect some delays (usually in the 1-5ms range) depending on whether I'm writing data or commands to the displays.  So I figure I've got a few options...

PS side (it doesn't seem Vitis/bsp files have a delay or sleep function)...

  1. Create a crude delay function in Vitis with a C for loop.  This always seems hokie and blocks other processing tasks so I really don't want to go this route.
  2. Use a timer and interrupts.  Good option as it can be tightly controlled and doesn't block other processing tasks but is a bit more complicated.

PL side (I don't want to do anything crazy like create a new custom AXI Quad SPI IP with delays as this seems really wasteful)...

  1. Use an AXI timer - I haven't used this IP so just another thing to learn and integrate into the Vitis app
  2. Use a Fixed Interval Timer IP block - I haven't used this IP so just another thing to learn and integrate into the Vitis app

For the PL items mentioned, I'm not sure they provide any real benefit (or code complexity improvement) over just interacting with PS timers.  Anyway - just wondering if anyone had comments.

Link to comment
Share on other sites

20 answers to this question

Recommended Posts

  • 0
16 hours ago, engrpetero said:

it doesn't seem Vitis/bsp files have a delay or sleep function

What platform are you creating your project on?

On Zynq-7000 and MicroBlaze, I never had an issue using sleep(seconds) and usleep(microseconds) functions in PS in a Vitis project. They are defined in sleep.h, which is generated, for example, as part of bsp\ps7_cortex9_0 folder.

The function usleep is implemented in xil_sleepcommon.c differently for each type of CPU. 
I know that on MicroBlaze usleep uses AXI Timer IP (if it exists in the HW) or a simple loop of instructions of known duration (that loop is written in assembly).

However, your question made me curious about how it is implemented on Zynq. 🙂
It seems that the approach on Zynq is similar to MicroBlaze.

For ARM Cortex-A9 used on Zynq-7000 the usleep is implemented as follows in cortexa9/usleep.c:

int usleep_A9(unsigned long useconds)
{
#if defined (SLEEP_TIMER_BASEADDR)
	Xil_SleepTTCCommon(useconds, COUNTS_PER_USECOND);
#else
	XTime tEnd, tCur;

	XTime_GetTime(&tCur);
	tEnd = tCur + (((XTime) useconds) * COUNTS_PER_USECOND);
	do
	{
		XTime_GetTime(&tCur);
	} while (tCur < tEnd);
#endif

	return 0;
}

A look into XilTimer library documentation revealed that in order to use a timer for usleep (which is a better option than a waiting loop), you need to have a timer available in the HW design and set a parameter sleep_timer in the BSP configuration. See the attached screenshot. With sleep_timer configured in BSP, the macro SLEEP_TIMER_BASEADDR will be defined in xparameters.h.

Viktor

BSP_config_sleep_timer.png

Link to comment
Share on other sites

  • 0

Hi Viktor!  Thanks for this reply!  I've been away from my project computer for a few days celebrating New Year's with my family.  I'll be back at it tomorrow and will check this file specifically.  I did search the entire workspace (in Vitis) for the text 'sleep' and didn't find anything.  But I didn't check the BSP first.  I appreciate the response and let you know what I find tomorrow (in the US).

Link to comment
Share on other sites

  • 0

Progress but a lingering issue.  I enabled TTCO (EMIO) on the Zynq in the project.  Re-did synthesize, implement, generate bitstream, export hardware.  Updated Hardware Spec in Vitis.  Modified the BSP settings for the standalone xiltimer as shown in the pic below.  I saved the platform project, rebuilt it.  so far so good.  I then rebuilt my Vitis application project.  When I do that, I get the following error in the Vitis Log. 

Oddly (to me), the error shows up in the xil-crt0.S file in the Hardware project (error shown in bottom pic) and the hardware project is set back to '(Out of Date)'.  When I go look at the BSP Settings, the 'value' of 'sleep_timer' is set back to none.  I looked at the documentation portal link again (https://docs.xilinx.com/r/2022.2-English/oslib_rm/Overview?tocId=yS2olESLOfOY3D3wq8NLmg) and didn't learn anything to address this issue.  I'll keep playing with it - I seem to remember issues with make files when using other peripherals/features) but any ideas?

image.png.c08bfc9038e514e4f1c8f3a1f60b04ff.png

 

C:\PXilinx20222\VitisWorkspaces\VW-E20\etest\ps7_cortexa9_0\standalone_domain\bsp\ps7_cortexa9_0\libsrc\standalone_v8_0\src/xil-crt0.S:97: undefined reference to `XTime_StartTTCTimer'
collect2.exe: error: ld returned 1 exit status
make[1]: *** [makefile:56: Edc20.elf] Error 1
make: *** [makefile:47: all] Error 2
 

Link to comment
Share on other sites

  • 0
Posted (edited)

Actually, it seems that even though I select ps7_ttc_0 in this dropdown and then click 'OK', the value isn't saved.  If I return to this screen immediately after clicking 'OK', the value is always set back to 'none'...

image.png.e5a3c524b4b37f68fa1b0a6e3fcd687c.png

Edited by engrpetero
(added image)
Link to comment
Share on other sites

  • 0
1 hour ago, engrpetero said:

Actually, it seems that even though I select ps7_ttc_0 in this dropdown and then click 'OK', the value isn't saved.  If I return to this screen immediately after clicking 'OK', the value is always set back to 'none'...

I tried to replicate the steps in Vitis on a Zynq project not using a timer for sleep, and it worked.
I did the following:

Opened platform.spr in the platform project.  Clicked Modify BSP Settings of the "standalone".

image.png.2572a4b18870bd958812b1303687f1d1.png

Enabled xiltimer library and selected TTC timer in xiltimer configuration.

image.png.0eb90ea71412e5569cfdfa5e0c377eb1.png

After I clicked OK on the BSP Settings form, Vitis did "a magic" and a section in xparameters.h changed from this

/* Definitions for sleep timer configuration */
#define XSLEEP_TIMER_IS_DEFAULT_TIMER

into this

/* Definitions for sleep timer configuration */
#define XSLEEP_TTC_INSTANCE 0
#define SLEEP_TIMER_BASEADDR XPAR_PS7_TTC_2_BASEADDR 
#define SLEEP_TIMER_FREQUENCY XPAR_PS7_TTC_2_TTC_CLK_FREQ_HZ

There is a confusing glitch in the Vitis UI because I do not see the TTC timer selected in the xiltimer configuration (same as you); however, the setting was propagated one level up to "standalone".

image.png.eee3377f14da6746a2ae952899808d83.png

Compilation went without errors. Please note that I tested only compilation, not that it works on HW. 😃

Viktor

Link to comment
Share on other sites

  • 0
On 12/29/2023 at 5:16 PM, engrpetero said:

 

PS side (it doesn't seem Vitis/bsp files have a delay or sleep function)...

  1. Create a crude delay function in Vitis with a C for loop.  This always seems hokie and blocks other processing tasks so I really don't want to go this route.
  2. Use a timer and interrupts.  Good option as it can be tightly controlled and doesn't block other processing tasks but is a bit more complicated.

Zynq-7000 supports the functions included in "sleep.h". Eclypse and other boards will support it without modifications to default BSPs - I've used usleep and sleep occasionally. Microblaze has been spotty with it depending on the tool version, but I think it works in recent versions (I recall it not having a complete implementation back in like 2016.4 or something). I am fairly sure that this driver relies on BSP settings like those mentioned to determine how to actually implement delays, but am unclear on the details.

Thanks,

Arthur

Link to comment
Share on other sites

  • 0

Thanks @Viktor Nikolov for the additional reply.  Indeed, I do see exactly what you showed in your pictures.  I should have looked more closely.  The UI does have the bug when you are setting the 'values' but the 'standalone' level does show exactly what you posted.  The xparameters.h file also looks exactly like what you posted regarding the XSLEEP_TTC_INSTAANCE and SLEEP_TIMER_xxx lines.  

Weirdly, even though I can build the hardware project with no issues, when I attempt to build the application project, I still get the following error in the HARDWARE project...

C:\PXilinx20222\VitisWorkspaces\VW-E20\etest\ps7_cortexa9_0\standalone_domain\bsp\ps7_cortexa9_0\libsrc\standalone_v8_0\src/xil-crt0.S:97: undefined reference to `XTime_StartTTCTimer'
collect2.exe: error: ld returned 1 exit status
make[1]: *** [makefile:56: Edc20.elf] Error 1
make: *** [makefile:47: all] Error 2

I'm curious @Viktor Nikolov (and thank you for the time you've devoted already to helping me) if after you build the hardware project, you built any application project based on the hardware project and saw different results from me.

image.png.ef8a6eaa878e20a7fbf423ae6884ed9d.png

 

I searched the entire workspace (both hardware project and app project) for 'XTIME_StartTTCTimer' and it only shows in two places in this xil-crt0.S file.  The file header does mention...

image.png.b329d907cc73e8b4df392d4cbe3addba.png

When I look at the hardware project files under xiltimer_v1_1, particularly the xiltimer.h file (though I looked at all the non-O files), there is no mention of an XTIME_StartTTCTimer method.  Searching XTIME_StartTTCTimer on the web didn't result in any useful leads.

 

image.thumb.png.7706a83ec9996e2cf65e0946cc55a1c3.png

Link to comment
Share on other sites

  • 0
1 hour ago, artvvb said:

Zynq-7000 supports the functions included in "sleep.h". Eclypse and other boards will support it without modifications to default BSPs - I've used usleep and sleep occasionally. Microblaze has been spotty with it depending on the tool version, but I think it works in recent versions (I recall it not having a complete implementation back in like 2016.4 or something). I am fairly sure that this driver relies on BSP settings like those mentioned to determine how to actually implement delays, but am unclear on the details.

Thanks,

Arthur

Thanks, @artvvb.  It does look like the functions contained in this header file will be just what I need.  If only I could get the undefined reference problem solved...

I'm still looking into it.

Link to comment
Share on other sites

  • 0

Worst case, creating a new workspace with a new platform created from the same XSA, and a new application project that includes the sleep header, will tell you whether the default BSP settings are sufficient.

Link to comment
Share on other sites

  • 0

More odd info (at least odd to me).  I didn't review all the search results earlier.  The XTime_StartTTCTimer() function is defined.  It's in xil_sleeptimer.h/c (see screenshot below).  But it's almost as if the function is 'ifdef' dependent as it's greyed out in Vitis.  However, at the top of the xil_sleeptimer.c file, I find #if defined (SLEEP_TIMER_BASEADDR) and SLEEP_TIMER_BASEADDR is clearely defined in xparameters.h...

image.thumb.png.9f38ec38d33e045e2e95e48b31a754f9.png

Link to comment
Share on other sites

  • 0
1 hour ago, artvvb said:

Worst case, creating a new workspace with a new platform created from the same XSA, and a new application project that includes the sleep header, will tell you whether the default BSP settings are sufficient.

When I update the XSA or change a BSP setting, I always do Project/Clean/"Clean all projects" + "Start a build immediately" to make sure everything is recompiled from sources.

I can only agree with Arthur. Vitis sometimes has weird issues. In the past, it helped me to start a brand new workspace and create a fresh platform and app projects because even Clean didn't help.

Viktor

Link to comment
Share on other sites

  • 0

Crazy tool.  I'll give that a shot and even just create a dirt-simple, plain project with a zynq and the items that represent the AXI GPIO for the Zybo-10.  No other content to see what happens with the sleep stuff.

 

Link to comment
Share on other sites

  • 0
Posted (edited)

Using Vivado 2022.2, I created a new very simple project based on the ZyboZ7-10 (see pic 1 below).  The only changes I made to the 'as-placed' IP were with the Zynq7 processing system in which I enabled UART0, TTC0, and TTC1 (see pic 2 below).  I ran synthesis, implementation, generate bitstream, export hardware.  In a new Vitis workspace, I created a new platform project based on the exported hardware.  I then created a new application project using the 'Hello World' template based on the platform project.  The options for the 'sleep_timer' are shown in pic 3 below.

  1. I built the platform project and app project with no errors.
  2. I updated the BSP settings to add the xiltimer item but left the 'sleep_timer' value as 'none'.  The platform project and app project build with no errors.
  3. I updated the BSP settings to set the 'sleep_timer' value to 'ps7_globaltimer_0'.  The platform project and app project build with no errors.
  4. I updated the BSP settings to set the 'sleep_timer' value to 'ps7_ttc_0'.  The platform project builds with no errors.  When building the app project, the platform project shows with the same error I posted yesterday (undefined reference to XTime_StartTTCTimer).
  5. I updated the BSP settings to set the 'sleep_timer' value to 'ps7_ttc_1'.  The platform project builds with no errors.  When building the app project, the platform project shows with the same error I posted yesterday (undefined reference to XTime_StartTTCTimer).
  6. I returned the BSP settings 'sleep_timer' value to 'ps7_globaltimer_0'.  The platform project and app project build with no errors.

Frustrating.  Any ideas?  Google hasn't been my friend searching for a solution to this problem.

I'm going to post on the Xilinx fora but I'm not expecting much there (you guys are more helpful here and again, anyone who answers questions, thank you very much)

image.thumb.png.eaf8e34afac3796d0f1da920a515c808.png

image.thumb.png.8fb081d62cc35a4f093d4b1f39144e41.png

image.png.74f5191b0fc5a4fb429cf1725ff5feea.png

Edited by engrpetero
Edited to add the comment about posting at Xilinx fora.
Link to comment
Share on other sites

  • 0
For what it's worth, when partitioning functionality into logic or software for a system involving both logic and a processor, I try to keep both sides as simple as possible.

For a Zynq or even an FPGA connected to a PC, let the logic resources do what they do best, and let the processor hw/sw do what your software environment does best.

FPGA logic resources are great for controlling timing. Since your displays are connected to the PL, I'd make the PS code as simple as possible and use logic to handle timing. You can always use a signal from the logic as a status, and either poll it (blocking code) or connect it to the PS GIC.

The problem with software is that implementing something with tricky timing might be simple when that's the only thing that your code is doing, but can get pretty hairy and complicated once all of the design functionality has been added, especially as you add more interrupt sources. Edited by zygot
Link to comment
Share on other sites

  • 0
6 hours ago, engrpetero said:

I updated the BSP settings to set the 'sleep_timer' value to 'ps7_ttc_0'.  The platform project builds with no errors.  When building the app project, the platform project shows with the same error I posted yesterday (undefined reference to XTime_StartTTCTimer).

I reproduced the same using Vivado 2023.1 and Vitis 2023.1.

After some poking around, I think that this is simply a bug in Vitis. It happens when you both enable xiltimer library and set TTC timer as sleep_timer in the BSP settings. This combination is simply not supported.

When you leave xiltimer library disabled and set ps7_ttc_0 as sleep_timer, the usleep() uses TTC0 as expected (I debugged the code).

However, I realized an important thing: I was wrong in my statement in my first reply. Regardless of what timer you use, the usleep() is always waiting in a loop!
I apologize for the confusion.

In the default setting, without any BSP modification, the following loop is executed when you call usleep():

int usleep_A9(unsigned long useconds)
{
	XTime tEnd, tCur;

	XTime_GetTime(&tCur);
	tEnd = tCur + (((XTime) useconds) * COUNTS_PER_USECOND);
	do
	{
		XTime_GetTime(&tCur); //This reads the global timer, which is always present
	} while (tCur < tEnd);
	return 0;
}

When you set sleep_timer to ps7_ttc_0 in BSP then the usleep() calls usleep_A9(), which calls Xil_SleepTTCCommon(), which does the following:

void Xil_SleepTTCCommon(u32 delay, u64 frequency)
{
	u64 tEnd = 0U;
	u64 tCur = 0U;
	XCntrVal TimeHighVal = 0U;
	XCntrVal TimeLowVal1 = 0U;
	XCntrVal TimeLowVal2 = 0U;

	TimeLowVal1 = XSleep_ReadCounterVal(SLEEP_TIMER_BASEADDR + XSLEEP_TIMER_TTC_COUNT_VALUE_OFFSET);
	tEnd = (u64)TimeLowVal1 + ((u64)(delay) * frequency);
	do
	{
		TimeLowVal2 = XSleep_ReadCounterVal(SLEEP_TIMER_BASEADDR + XSLEEP_TIMER_TTC_COUNT_VALUE_OFFSET);
		if (TimeLowVal2 < TimeLowVal1) {
			TimeHighVal++;
		}
		TimeLowVal1 = TimeLowVal2;
		tCur = (((u64) TimeHighVal) << XSLEEP_TIMER_REG_SHIFT) | (u64)TimeLowVal2;
	}while (tCur < tEnd);
}

In both cases, the timer is simply used as a source for measuring the number of "ticks".

It is not clear to me why using TTC timer would be preferable to the global timer for usleep.
After reading this very nice article explaining Zynq timers I understood that the source clock for TTC may come from various sources. So you can build a custom board and feed TTC with a frequency of your choosing from an oscillator of your choosing (see this Xilinx forum post).

Study of xtime_l.h and xparameters.h revealed that on Zybo Z7 ticks of TTC timer have the frequency 111.1 MHz and ticks of the global timer have the frequency 333.3 MHz (half of the CPU frequency).

Viktor

Edited by Viktor Nikolov
Link to comment
Share on other sites

  • 0
Posted (edited)

Sorry for the late reply - I hadn't noticed your post, @Viktor Nikolov yesterday.  I spent yesterday learning exactly what you posted. 😀  I spent a lot of time with the global timer and the triple timer counters (TTC) to get something 'debuggable' in Vitis only to arrive at the same conclusion that the included drivers are simply waiting in a do loop which isn't a lot better than a for loop (the do loop looking at the timer counter value would at least let you do accurate timing without the need to do trial and error on a for loop max iteration value).  

Edited to add this line...  I could use a TTC with an interrupt to avoid the do loop.  That's how I use the scutimer right now for other tasks anyway.

So using a timer does provide a solution.  Now I'm reconsidering @zygot's last post.  I totally agree with zygot's post about the using the PS and PL based on their respective strengths (and ease of implementation).  The PS code for this project is pretty complicated so keeping the PL functionality easy to use and 'complete' on its own is something I've been striving for.  And to get the display functional (if not optimized), I was looking for the PS solution to time delays so I could just use the Xilinx provided AXI QSPI out-of-the-box for sending messages.

But now that I've had some time to think about it, I'm not sure of a reasonable way to use the AXI QSPI for communication while also including delays on the PL side since they would seemingly need to be after the AXI QSPI block. I'm still thinking about this though.

Edited by engrpetero
Link to comment
Share on other sites

  • 0
I don't use AXI much, even for ZYNQ designs. The whole idea behind AXI is that it can accommodate fast and slow peripheral interfaces on one bus. That's why it's so complicated. I tend to think of things like a display controller implemented in logic as a functional block. Usually, there's some data exchange involved in functional blocks. I try to decouple data pipe timing and complexity from functional block timing. Usually this involves the simplest data pipe implementation at the fastest speed and some data buffering as the functional block either sources or drains data.

If you want to use a third party IP, such as AXI QSPI, and not have to create your own design, then you are stuck with the limitations of that IP... unless you can figure out a work-around. If one end of a data pipe is connected to a processor or ZYNQ PS, then you will be using some sort of AXI IP as a data pipe. I tend to go with the streaming IP for a number of reasons.

There are a lot of things to consider when designing a system. People in a hurry tend to go with what they are comfortable with. This isn't always the best option. Unfortunately, you may not know it until near the end of a project when everything needs to work. This has always been a problem for complex systems involving logic design and programming. Experience is the best guide.

ZYNQ has lots of functionality and resources like timers. Unfortunately, the tools change from version to versions, or add bugs, and make using these resource difficult. I try to avoid the tool version dilemma to the extent that I can.

One possible way to implement a fixed time delay in software without blocking processor execution is to use the PL wisely. One could write a delay value to a PL timer block via AXI and have the timer block set/clear a signal that is visible as a gpio pin to the PS once the timer reaches that count value. A pin state change could initiate an interrupt. Doing this the first time will probably be something of a chore. But once you perfect the technique you have something that a tool version change won't cause a major headache. The advantage of all that work is that you have something that you understand and can control for yourself. I have no problems with quick and dirty solutions, especially for prototyping ideas.. as long as they work and implementing them isn't beset by roadblocks. The quick and dirty offerings of FPGA vendor IP are too often not as advertised.

Something to consider.... Edited by zygot
Link to comment
Share on other sites

  • 0

Ahh, yes, I have a few tools and those are the default ones I go to while trying to acquire new tools.  I am in a hurry but am also fortunate for now - I have lots of other things going on so this design can be iterative and improved on.

Being a mostly PS side person delving more and more into the PL side (because I find it very interesting), I am not sure how to efficiently (OK, 'maybe efficiently) get from the PS to the PL *except* by using an AXI peripheral.  I do love to read and have no problem doing research.  You have any pointer links I could wade through that show other ways to accomplish PS/PL interactions besides AXI?  

Link to comment
Share on other sites

  • 0

My two cents:

Zygot has posted about using EMIO for this kind of communication before and IIRC had posted a guide in the project vault.

There are several different CSR generator tools floating around online that make it quite a bit easier to create AXI peripherals and drivers. From personal experience (personal opinion about the subset I've tried), cheby (https://gitlab.cern.ch/be-cem-edl/common/cheby/-/blob/master/doc/cheby-ug.pdf?ref_type=heads) and rggen (https://github.com/rggen/rggen) are easiest to use when also doing block design work, since VHDL files can be added to a project and directly instantiated as RTL modules, and generated drivers can be directly copied into a Vitis workspace. Cheby uses pretty simple YAML definitions of a register map and can be invoked from a command line like git bash (I'm primarily a Windows user at this point).

AXI GPIO can also be used to directly control ports of RTL modules as well. This one is clunky and feels like a workaround though. See the screenshot below.

image.png

 

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