Friday, June 30, 2023

User interface test verification part 6


The H2F bridge remains stubbornly frozen, nothing I do seems to unlock it and make it ready. I prefer it because my logic was shaped around the 64 bit wide interface it provides, but I already am able to communicate over the H2F LW (lightweight) bridge. 

It has a lower thruput and is only 32 bits wide, but my only use for H2F is to load a virtual cartridge file into RAM once when we select it and then unload its contents after the drive is switched off at the end of a session (or power fails). Thus, performance is not a major issue. 

I hacked up my design to transfer only two 1130 words (a 32 bit transfer) over the H2FLW bridge, but not downrating the F2SDRAM bridge where I access RAM from 64 bit at a time. No change to my load and unload transactions up in the Linux side. Too much in the disk emulation depends on the 64 bit RAM width to change it. I just need to buffer words and combine the pair to send on as a pseudo-64 bit block. 

If I figure out the H2F issue later I can always go back, doubling the words per transfer, but this seems to be the most direct route to having things work. I had to change my code for the Linux side as well as the loop control and addressing logic in the FPGA side, but it is worthwhile to have some success.

The magic of how these bridges work on the Cyclone V System on a Chip is that the real memory address range FF200000 to FF200007 is passed to my original code that sends commands and status over H2FLW. The range FF200008 to FF20000F is passed to the modified logic which moves pairs of 1130 words between H2FLW and F2SDRAM interfaces. 

This ultimately places the megabyte of virtual cartridge file data in the last 1MB of the SDRAM. Linux is blocked from using that part of the SDRAM thus it is my dedicated buffer for the disk emulation. The load operation memory maps the SD card file that is a cartridge image into virtual storage, where I loop transferring the data through the FPGA to its home in the SDRAM. Unload is the reverse, grabbing the content two words at a time from SDRAM and letting it be read from the Linux app at the magic address. 

One minor complication is that addresses like FF200000 are real thus I have to memory map it into virtual storage in order for my application to write or read over the bridge by accessing the virtual address that is mapped to FF200008. SDRAM addresses are their own thing, separate from real and virtual addresses of the processor (MPU). See the diagram below.


The load operation cycled through a full virtual cartridge load. It was not that slow a process. However I could see that the FPGA and the Linux app were not in sync, because when the application through it was done and checked status, it was sent a code that implies the load was still underway. 

That is next to debug, but it does show that I can communicate using the H2F LW bridge and ignore the troublesome H2F version. One strategic weakness is that my H2F LW bridge operates on 32 bit transactions but the memory interface to SDRAM is 64 bit. My logic may be hanging up dealing with pairs of transactions - more instrumenting is needed to figure this out. Still I am not moving forward which is wonderful. 

Tuesday, June 27, 2023

User interface test verification part 5


When configuring the HPS using the Platform Designer tool, a ton of data is entered about the memory - timings and how the address bits are assigned across banks, chips, rows and columns. In another spot one requests the F2SDRAM bridge that allows the FPGA to read and write the RAM using the level 4 interface of the Hard Processor System (HPS) ARM based computer. 

I guess I naively believed all that information was carried over so that the bridge merely needed to be activated in order to issue requests to it. What I face is a long list of registers with values that must be correct and zero guidance on what is needed to move from the Platform Designer entered information to a working bridge. 

control registers for the F2SDRAM from the HPS side

I spit out the current contents of all of those registers as I initialized things. As far as I can tell the values are good but my attempts to access SDRAM were still failing. Just in case it was an issue with writing to the RAM, I flipped around to use the unload process which first reads from RAM via F2SDRAM bridge and then makes that data available to read from the H2F bridge. Same type of issue, stalled. 

Digging through the thousands of pages relevant to the bridges I find all sorts of registers and bits that might be related to the problem. There are bits in registers to force the use of secure masters or slaves only. There are fields that specify how deep the write FIFO will get before an output is executed over the bridge. Essentially several hundred fields related to the bridges.

I worked out the method to turn on the bridge allowing my FPGA logic to write to the SDRAM. It involves making some settings in control registers during initialization of my application. Now that works but my H2F bridge, the link that my application uses to transfer blocks of four 1130 words between Linux and FPGA sides, is also not working. The wait request signal is on immediately which blocks the FPGA side from sending anything. Worse, when I attempt to read or write from the Linux side, we hang and have to power cycle. Same basic problem as the F2SDRAM bridge issue, but now for H2F instead.

I dug through mountains of documents, various posts by people I discovered through searches, reviewed code and the best I could come up with was to flip on bit 3 of a particular register called l3remap. I gained accessibility by memory mapping this but whenever I attempted to write to it I generated a bus error terminating the application.

Other places have mentioned the provided command - bridge_enable_handoff - run during U-boot as the system begins to come up.  That attempts to branch to an address that is labeled as something to enable F2SDRAM but the code terminates blocking the rest of the scripted commands from executing. Thus, crappy quality code which blocks the enabling of the bridges which otherwise would have presumably been done automatically.

I entered U-boot and used a command editenv to edit bridge_enable_handoff  but even after I issue a saveenv to save the changes to nonvolatile memory, the next time we power up it is back to the same old defective code. 

So many things to debug. So damn many things that have zero, zip, nada, nothing to do with my project, my code, my logic. Just a giant burn of time and energy to use the capabilities of these products as they are described. 

Sunday, June 25, 2023

User interface test verification part 4


I instrumented the FPGA side using the LEDs on the board to indicate what stage we were at in handling the H2F bridge during a load operation. I need to see why we are stalling the Linux side rather than taking each block of four words and writing it up to the SDRAM last megabyte. 

We were stalling issuing a write on the FPGA2SDRAM bridge, which is where we can read or write the contents of the DDR3 memory directly. Since we reserved the high MB (actually got 256MB reserved because of a restriction in the Linux kernel I am using), we write and read from that area as our own dedicated RAM buffer for a virtual disk cartridge image. 

Doing a bit of digging, it appears that while the other bridges are enabled and working (H2F and H2FLW where I transmit data and command/status respectively between Linux and the FPGA side), I have to take explicit action to turn on the SDRAM bridge. This involves writing bits into a memory location which is the control register to take that bridge out of its default reset condition. 

I added the code into my initialization module to make this happen and went back to test some more. Unfortunately the results were no different. The SDRAM controller is holding wait request high which blocks my first attempt to write to the memory. 

When I look to the control registers - of which there are quite a few - I see a myriad of configuration settings. Decoding which address bits select row versus column versus bank versus chip, as just one example. Once again the wholly inadequate level of documentation that comes with the Altera/Intel System on a Chip, in spite of tens of thousands of pages written, provides yet another challenge. 

This seemed so easy. There is a demonstration that implements a processor in the FPGA side which makes use of half the RAM using the F2SDRAM bridge, just like I want, while the ARM HPS system is running code using the other half of the RAM. Sadly it is not a regular Linux running on HPS but instead the standalone stuff they provide. Therefore there are some big gaps between what is clearly possible from the demonstration code and what I will need while running an actual Linux system on HPS. 

User interface test verification part 3


I did find the problem with the routine to open the filename. I had forgotten to append the absolute file path to the directory where the cartridge images are held. That corrected, it did open the file, map it into memory and then proceed to the next step which is the load routine. 

It was at this point that execution went awry and the Linux system hung. This is understandable as the load routine is sending the 521,304 words of 16 bits each down to the FPGA which wrote them to the last physical 1MB of RAM. I however had not yet informed Linux to avoid that last block. This means that Linux was using the upper megabyte at the same time that my FPGA was writing things in it. 

The process of reserving the upper RAM is a boot time parameter I set up from the U-boot loader that precedes the loading of the Linux kernel. I can enter U-Boot, set a command that gives Linux only 1023MB of RAM and then save it for every subsequent boot by issuing saveenv. I performed this step, verified that it was permanent and then went back to testing with my design. 

I see during the loading of Linux that it rejected the 1023MB memory and downgraded to 768M for RAM. This is pretty wasteful but I still have enough memory for Linux to handle this work so I won't be digging into the cause and possible solutions. A message mentioned having a HIMEM enabled Linux kernel, which may be a way to overcome this limit.

The system is hanging - it acts as if the H2F highspeed bridge is not operational. I was successfully using the lightweight H2FLW to send and receive commands, but this is the first attempt to use the 128 bit wide H2F pathway. 

What should be occurring here is a loop which reads through the recently opened SD card file with the virtual cartridge selected, writing four 16 bit words at a time of the 521, 304 total in an image. This will be 130,326 iterations through the loop. Each time we store the four words we read from the memory mapped SD card file into the location for the H2F bridge. 

The FPGA logic I wrote is listening to the H2F bridge and will read each block of four words as it is sent from the Linux side. It then writes those four words out to the last 1MB of RAM on the board. To ensure that we don't over run the FPGA side since the RAM is slower than our loop, we have a hold off signal that the FPGA sets that holds the Linux side bridge from accepting any more blocks. I suspect that my H2F bridge sees the hold off signal and waits perpetually. 

I plan to debug the load and unload next, so I substituted a fake set of load and unload modules that simply return with a success indication. Given that, I completed my testing and was certain that pushing the Selection Toggle pushbutton did flip back and forth between selected and simply highlighted status. 

User interface test verification part 2


My testing uncovered a couple of subtle flaws but I have the interface responding exactly as it should to all the up and down rocker presses. I did some edge cases with exactly one screen and only one file just to double check, but everything here is solid. 

I even injected a Heads Loaded signal which triggered my program to throw up the picture of the cartridge and go into disk emulation mode. I wasn't intended on debugging this yet so I stopped with the cartridge visible. 


The Selection Toggle Button flips the state of the filename that is currently pointed to by the user interface. As we move up and down, without having selected a file, we are simply moving the highlighting among file names. Pushing the button will flip that entry from highlighting to selected, as well as triggering actions such as loading the file into the system for use by the IBM 1130. 

If we have already selected a filename, then pushing the button turns off the selection, allowing normal movement of the up and down arrows. 

When I first pushed the selection with a certain file highlighted, I received a failure from my module that was attempting to open the virtual cartridge image file. That is my next session of debugging. 

Saturday, June 24, 2023

User interface test verification part 1


I built up sequences of up and down presses in order to validate the operation I expected. The concept is that we see a list of up to 13 file names on the screen with one of them highlighted with a gray background. The arrows move the highlighting up and down the list.

Since we will have more than 13 virtual cartridge files on the SD card, we also have to scroll when we move beyond the 13 that are currently displayed. If the last line is highlighted and we push down, we would display the next file names (up to 13) and highlight the first one. If we are have highlighted the first file name on a list and push up, then we will display the prior up to 13 file names with the last line highlighted. 

This has to deal with the cases where we are at the first line, push up, but there are no more files above this in the list. It has to deal with the case that we are trying to move down from the last line when there are no more filenames in the list below it. There are also cases to consider when we don't have a full 13 filenames after scrolling, either displaying a short list for a down scroll or displaying the first 13 with an up scroll if are reaching the beginning of the list.

Finally, we disable scrolling with the up and down arrows if the highlighted line on the screen is also selected - cyan background and red lettering instead of gray background/black text. This means that we have loaded this file into the system and it can be accessed when the disk drive spins up. In that case we don't want to scroll the selected file off the screen so when we are at the top or bottom line, the up or down buttons do nothing. The file moves so that we see 0 to 12 files before it and 0 to 12 files after it on the screen - depending on which line we have moved this filename to. If it is at the third line, for instance, then we see the two files before the selected one, our selection, and then the next ten file names on the screen. 

If the user wants to view other file names outside of the near neighbors of the selected one, they have to push the Selection Toggle pushbutton in order to unselect the file before we can provide scrolling. 


The redraw of the screen is a bit pokey and potentially irritating. I first attempted to bring up the SPI speed as much as possible. While the documentation with the LCD Module says that it has been tested up to 60MHz, even at 40MHz the display can't keep up and simply flickers a bit. Whether that is a wire quality issue, an issue with my PIO altered LCD Select and LCD Data/Command lines, or whether the module really can't keep up, the simplest solution was to begin backing off speed until I achieved reliable results. 

The speed of the SPI is set by a divisor that slows an internal 200MHz clock down. The divisor must be an even number, per the specifications of the SPI module, thus my choices are even integers larger than 5 (40MHz). The choices are 33.33MHz, 25MHz, 20MHz and slower rates. I knew that 25MHz worked fine so I tried 33.333MHz which appears to be working reliably. 

The process for drawing the screen involves a clear operation, writing 307,200 bytes of black to the screen, then writing the 13 lines of text. The screen clearing takes about 75 milliseconds thus overall we are looking at a fraction of a second at this speed and more than a second at the prior rate I was using. Even if I have to use 25MHz, if any flakey behaviors surface, it will still slash 25% off of the time to draw the screen after every up or down press. 

I realized that I could optimize the redraw, having one time filled the screen with black pixels. Moving the highlighting to a different line only requires me to redraw the previous and new lines, leaving all the other lines on the screen alone. Scrolling can be done with a clear of the screen then drawing all 13 lines. I implemented and tested this. 

I did find squirrely results at 33+MHz so we drop back to 25MHz for production. I have some annoying issues I am still chasing to get the screen to look and respond just right, but will move on to testing this tomorrow.

Friday, June 23, 2023

Setting up testbed to exercise the user interface for debugging


This unit is in my home not at the workshop and therefore is not wired to an IBM 1130 disk. I have a bit of work to finish wiring the cables from disk controller to my level shifter, then from the level shifter to the DE10-Nano board.

The rocker and pushbutton switches won't arrive until tonight and then will need to be wired to the rest of the gear. 


Once the system has been initialized, the UI loop begins. It starts by drawing the interface screen with the first 13 file names discovered on the SC card and with the first filename highlighted but not selected.

The loop watches for one of five signals to be activated. These are:

  • Power Failure
  • Heads Loaded
  • Rocker Switch UP pushed
  • Rocker Switch DOWN pushed
  • Selection Toggle button pushed

A power failure signal will tell us that we have lost the 12V line from the IBM 1130 and are running on our battery backup. We will break out of the interface loop which eventually will close down the bridge and other interfaces. Meanwhile a subtask monitoring the power failure signal gives us a few minutes before it commands Linux to do an immediate graceful shutdown. 

When the Heads Loaded signal goes on, we are being told that the physical disk drive was switched on, the dummy cartridge inside is spinning, and the controller attempted to command a solenoid to force the heads down onto the disk surface. If we have a selected filename, then we have already loaded the contents of that virtual cartridge onto the reserved RAM through the FPGA side of the system. In this case, we set the FPGA logic to emulate disk operation by sending a disk run command over the bridge and we send a signal to the disk controller that causes it to go into Ready state. Once it is ready we display a cartridge image on the screen and go into an inner Disk Operating loop.

When the Rocker UP or Rocker DOWN signal arrives, we move the highlighting up or down in the last of filenames from the SC card. If needed we scroll the screen as we can only show a max of 13 at one time. If a file is selected, we don't move off the screen but we can move the highlighted/selected entry upwards and downwards among the 13 spaces on the screen, exposing 0 to 12 filenames on either side of the selected one. 

When the Selection Toggle signal arrives, if we had previously selected one of the filenames then this just switches off that state, going back to showing it with simple highlighting. If the filename is highlighted but not already selected, we complete that action. This involves opening that virtual cartridge file, then sending a load command over the bridge and passing the contents of the file down to the FPGA to be written into the spare RAM. Once done, we show the filename with the selection formatting. 

Once we complete acting on one of these signals, except when we go into the inner Disk Operating loop, we redraw the screen are begin again at the top of the loop. Thus we are spinning around looking for one of those signals to take action. 

The Disk Operating inner loop is in control when we have the drive activated using one of our virtual cartridge images. In the inner loop we look at only two signals:

  • Power Failure
  • Head Loaded off
When Power Fail happens or the Head Loaded signal becomes false, if there had been a virtual cartridge loaded we have to perform an unload. We first send a disk off and then an unload command over the bridge and our FPGA logic will retrieve the cartridge information from RAM including any changes made by write operations from the IBM 1130. This is written back into the SD card file whenever we have had a change in contents - rather than replacing the entire contents. 

Thus the inner loop ends with either of those signals being detected and acted upon. If it was a release of the Head Loaded signal, we go back to the top of the interface loop, drawing the user interface screen and waiting for one or more of the five signals. If it was a Power Failure, that main loop then will end as well and we go through the graceful shutdown of the system, closing files, releasing memory and stopping the bridge. Meanwhile a subtask monitoring the power failure signal gives us a few minutes before it commands Linux to do an immediate graceful shutdown. 


I intend to redirect the points where the program will test the five signals, instead pointing at variables in the program that I can control. I can set these to act like button pushes, head loads, head load deactivation and power fail signals. 

At the bottom of the interface loop, where it would be returning to the top to start again, I will have a counter and some automation based on the value attained in the counter. We also slow this down with a 2 second pause for each pass of the loop which allows me to watch it in operation. 

I will be moving the highlight up and down, ensuring that it displays properly and handles all the edge cases. I will then toggle on selection and watch to see it request a load

I will deselect and move to a different file, again doing a selection. This will verify that we properly release the first file. 

Then I will turn on the Head Load signal to see that it drops into the proper Disk Operation loop. I can bring it out later by removing the Head Load and watch it request an unload

Finally I can test by having no file selected, asserting Head Load then later raising power fail which should not try the unload but still end the program.


I am slogging my way through integrating and testing all of this, so that I can get into the core of the user interface and test it our as detailed above. 

Thursday, June 22, 2023

Revamping the screen layout now that touch interface is out of the design


The bottom of the screen was set up with two arrow buttons and the selection rectangle button. Now that the touch interface will not be used, that space is available for other information, either the file name list or some status messages. It was roughly 40% of the screen space tied up in the user interface objects that I can now use more effectively for output purposes rather than input. 


I ordered a rocker switch that implements two momentary positions, on-off-on, which will be oriented vertically so that it intuitively signals its use to scroll the file list up or down by one entry. I have also ordered a larger round button that will toggle the selection of the currently highlighted filename. 

Up/Down Scroll

Selection Toggle Button


I will expand the input PIO function in the FPGA side of the design to detect the closure of the switches in the rocker switch and pushbutton. I can give up two external pins, one in the input PIO and one in the output PIO, as they were used as slave select for the touch controller chip and to test for a touch having occurred. On balance, then, this uses only one more pin than the prior approach. 


I tried a larger 20 point font to see what the filename looks like at that scale. I found it more readable and adopted it. That gives me 38 characters across the screen in a line and room for slightly more vertical spacing between lines. This still works with a maximum of 13 file names on a screen. 

I then conducted an exhaustive series of tests for various scrolling amounts and conditions of highlighting and selection. The behavior I intend is to have the system come up with the first 13 filenames in the directory on the screen, with the highlight position at the top line. 

20 point font, showing highlight of first filename

Each time the rocker switch is pushed downwards, the highlight will move to a lower line, e.g. from 1 to 2. When the highlight is on the last line of the screen and a further down is requested, we scroll the highlighted filename to the top and display up to 12 more filenames on the screen. Attempting to move down when the last filename in the list is highlighted does nothing.

Each time the rocker switch is pushed upwards, the highlight will move up by one line. If it is at the top of the screen but there are files above it that are not visible, then we scroll by putting the highlighted line at the bottom and displaying the previously unseen filenames above it, up to 12. Attempting to move up when the first file in the directory is already highlighted has no effect. 

When the Selection Toggle button is pushed, the highlighted filename entry becomes selected. It will be displayed with different background and font coloring to indicate this. The logic will open the file on the SD card and load its contents down through the FPGA into the hidden RAM so that if the drive is powered up and attempts to load disk heads, this data will be accessed as if it were a physical 2315 cartridge mounted in the drive. 

Scrolled and with the entry selected

Pushing Selection Toggle when the highlighted entry is already selected will invalidate the downloaded cartridge image and show the line in its normal highlighted state. When scrolling by using the up and down presses of the rocker switch, we will never move the highlighting off the screen. In other words, if we have 100 file names on the SD card and we have scrolled down to highlight the 30th file name, then toggled to make it selected, we cannot scroll down to move the 30th filename off the list. 

If it is at the top because we are pushing up, once it hits the top all subsequent up pushes are ignored unless the file is deselected by toggling. Similarly pushing down when a selected entry is at the bottom of the screen won't have any effect. One has to deselect in order to move around to filenames more than +/- 12 entries from the highlighted filename. 


Part of the planned user experience is to have the screen show a picture of a 2315 cartridge while the drive is in operation. This happens when a filename has been selected by the user toggling on a highlighted name, then the drive is switched on and begins spinning up the dummy physical cartridge that is inside it. Once the drive is at speed it attempts to load the heads down onto the disk surface and when that physical movement is detected by a microswitch, it makes the drive ready for access by the IBM 1130.

With the modification to the drive, when the switch is set to virtual mode, the signal to load the heads does not activate the solenoid to really make that happen. Instead, our FPGA logic sees that signal, knows we have a virtual cartridge loaded into the hidden RAM, so it sends the signal substituting for the physical microswitch. This turns on the Ready lamp for the disk drive and it can now be used by software in the 1130. 

To indicate that the virtual cartridge is loaded, once we see that head load signal from the drive arrives and the user has selected and thus loaded a file, we replace the user interface screen with a picture of a 2315 cartridge. When the drive goes out of ready, usually because the user has switched off the drive, we see that head load signal go away. That is the signal to remove the static picture of the cartridge and go back to our user interface screen. 

My first test produced some garbled image on the screen, but I quicky spotted the problem and corrected it. I see the bitmap - although it is reversed in both vertical and horizontal orientation. Since this is a placeholder to be replaced by a photograph I will take of a cartridge, I didn't do anything to flip it.

Placeholder image on the screen

Wednesday, June 21, 2023

More tests with the touch controller to understand why it isn't working, until I declared it a dead end


I am reaching the point where my interest in continuing to battle the XPT2046 touch controller chip is disappearing. There is a solid alternative making use of physical buttons for the up, down and selection toggle functions, thus eliminating the need for the stylus and touch operation of the user interface. 

Frankly, the stylus would be easy to lose inside the 1130, particularly as they will be operated by multiple people in a museum setting. The screen is small and human fingers are therefore relatively large, but it might work out to simply tap with fingers. 

The Arduino boards I have in stock operate on 5V signal levels, not the 3.3V used by the Terasic DE10-Nano and the LCD Module. I would have to wire up level shifters, which adds time to setting up for an Arduino based research effort. 

I committed to investing only a few more hours today trying out different changes to communicate successfully between the DE10-Nano programs and the touch controller. I am a volunteer at Cape Canaveral Space Force Base guiding press, VIPs and workers who registered to watch a launch. I have an assigned location, in this case about 7000 feet from the pad where the Delta Heavy will launch in the early morning hours. Until I leave sometime after midnight to enter the base and drive up to the viewing spot I am supporting, I can bang away varying things to see if I can talk with the chip.

I Googled to see if others had experienced issues with the touch controller and did find a variety of complaints and some solutions. Most made use of existing libraries on platforms like Raspberry Pi Pico and Arduino rather than directly coded access like my code. 

A few mentioned never getting the touch part of the LCD Module to work, speculating that crappy clone or pure counterfeit chips were on some of the inexpensive modules being sold on eBay - where of course I purchased my modules. I guess that is one possible explanation that would encompass two modules behaving exactly the same. 

One mentioned having to change the SPI mode to communicate with the chip, different from the mode CLEARLY drawn on the data sheet. Another expressed challenges getting the high resolution 12 bit sampling to work but received 8 bit results more reliably. Since my last tests are more of a desperation mixed with a choice to invest a few hours tonight to wrap up the controller communications test, I focused on these possibilities in my remaining experiments.


First up was the easiest, simply changing the command to request a low resolution 8 bit value. I did this with both the temperature sample and the X axis position sample transactions.

My temperature measurement command was 0b10000100 which is composed of the start bit, a three bit selector of 000 for temp measurement, a 1 bit resolution selection of 0 for high res, a 1 bit measurement type bit of 1 indicating single ended electrical sampling, and the last two bits are the default behavior to power down and remain listening for touch interrupts after the data was returned.

That command is altered to 0b10001100 to request a single ended measure of temperature with low resolution instead of 12 bit resolution. If the issue was failure of 12 bit sampling, I should see values with this command instead of the return of 12 bits of 0 that I have been experiencing.

The X position command has a measurement selector value of 101, so that the command for differential measurement of X position at low resolution would have the command 0b11011000 instead of the 0b11010000 that I had been using for high resolution requests. That should spit out up to eight non-zero bits on MISO indicating that the chip responded correctly to this. 

Whether low or high resolution, we got back nothing but zeroes. This is not the explanation. 


The LCD controller chip, on the same LCD Module as the touch controller, definitely works properly with clock polarity 0 (idle state low) and clock phase 0 (sample incoming data bits on the rising clock edge). This is called mode 0 by some people. It would be ironic if I not only have to change the speed of the SPI link to talk to the touch controller but also the SPI mode, but that is indeed possible. I call a function to disable the link, switch baud rate and re-enable, so why not change the mode as well?

I set up a sequence of tests, covering all the permutations of polarity and phase. Mode 1 has the same low clock polarity but switches clock phase to sample incoming data on the falling edge of the clock. This can have the result of missing the first bit of a byte if the mode set for receiving has a different mode than the sender, but in this case it won't matter!

That is because the wonderful XPT2046 chip is not really byte oriented, it is a bit stream device. Once it has completed any prior sampling of data it was performing, it just watches incoming bits until it sees a 1. That starts the sampling and then analog to digital conversion. After a 1 bit kicks of a transaction, the next 7 sequential bits contain the rest of the command, selecting which measurement to take, single-ended versus differential, low versus high resolution, and behavior once the sample data is returned. 

Following the 8 bits that began with a 1 the chip needs one more bits (clock cycles) to finish sampling before it spits out the 8 or 12 bits of low or high resolution measurement. Thus we have 8 bits of command, 1 bit of padding, then 8 or 12 bits of data. 

Immediately after the last data bit is received, you can send a 1 bit to start this up again - therefore not on an even byte boundary at all as we had 17 or 21 bits total from the last sample transaction. When dealing with microprocessors and protocols like SPI that are framed in units of a byte, we simply continue to send 0 bits after the last data bit was received until we count out the full 24 bits, 3 bytes of a transaction. 

If we were driving the chip with discrete logic, an FPGA or something else that didn't require even byte boundaries, we could operate with the streams of 17 or 21 bits. Fortunately, this means the XPT2046 would be insensitive to errors where SPI mode mismatches put the start bit as bit 1 of a byte instead of bit 0. 

The controller chip really doesn't care about bytes and simply reads incoming bits until it sees a 1, after which it interprets the next 7 as the command parameters, ignores the following clock cycle, and then returns 8 or 12 bits of data. If we are out of alignment, I will still receive the 8 or 12 bit value, just shifted by a bit. We already have to deal with the measurement sprawled across two bytes when receiving it on a byte oriented system or link, so there is shifting involved to turn it into a right justified 8 or 12 bit unsigned integer. 

All four modes were tested with identical results. Nothing happens at all. Not even sure if this is a real chip or just some inert junk inserted by the penny ante chiselers of the low cost electronics manufacturing world, who will make counterfeits or use substandard shoddy substitutions to wrest another penny or two of profit. Often done in societies that theoretically are not profit driven. 


The data sheet shows that a pin called Busy should go high in the midst of a sample transaction for a short interval. That pin is not connected on the LCD Module but I was able to put a mini grabber on the lead and hook it up to the oscilloscope. Triggering the scope on rising edge, I will be able to tell if the chip is going through the motions at all or just a silicon brick. 

However, the form factor of the controller chip is small enough that my grabbers wont fit on a pin without shorting to adjacent ones. I would need to tack solder a wire to the pin, something that would require me to drive over to the workshop to accomplish under the stereo microscope. It just isn't worth it. I am proceeding rapidly to yank all the touch panel stuff out of the project and build it around physical interface buttons.

Still unable to 'touch' the touch controller chip


I found a tiny footnote in the datasheet under a graph that showed the variation in sampling rates due to factors such as temperature, but with small variation around 100,000 to 125,000 per second. The footnote had some cryptic formula that showed fSampling (125K/s) * f16 = f2M which I believe tells us that when this performs 125,000 samples per second producing 16 bit output from the Analog to Digital Converter (ADC) it would be clocked at 2MHz. 

I suspect that the ADC uses small capacitances to retain the voltage while it achieves the digitization during the 16 clock cycles. If it is run too fast, it can't produce a result. If it is run much too slow the sampled value bleeds away before operation. For this Goldilocks chip, the frequency must be 'just right'.

I imagine this can be clocked as slow as 1.6MHz to accommodate the low end of the sampling performance which necessarily must be supported by enough hold time in the ADC to still produce results. That is the frequency I planned for using the divisor 0x007C (124) to generate 1.613MHz for SPI from the 200MHz master clock. 

In reality the chip does not support a full 100K to 125K samples per second because it has the additional 8 bits of the command byte that has to arrive before the 16 bits of data output will be produced. Thus the 10 microseconds it takes at 1.6ish MHz also has a 5 microsecond period for the command byte. Thus the max rate we can request sampling is somewhere over 66,000 times per second and each sampling SPI transaction takes 15 microseconds. 


I set up a tight loop issuing sample commands for the temperature inside the chip, the command value being 0x84. This asks for a single ended voltage measurement across the temperature sensitive junction, at high resolution of 12 bit values (that come in the 16 bits returned along with four bits of 0 padding), allowing the chip to power down after each sample and to emit touch interrupt pulses as the screen is touched. Since the chip can 'instantly' power back up, this is the normal mode for a continuous stream of sampling. 

Nothing came back but zero bits. The same result I get with reading the X or Y coordinates. Is this chip working at all? I need to purely look at what is going in and out of the touch controller chip. 


I wanted to use my four channel scope to see SCLK, MOSI, MISO and the software generated SS at the same time. I made that happen, with the MISO line removed from the FPGA and directly hooked to the scope so that I could see what it was doing without any interference.

As soon as I assert the SS line but dropping it to zero, I see the MISO line drop from logic high to logic low where it stays throughout the entire transaction. 

I measured the SCLK signal and verified it is at the 1.6MHz rate needed to talk to the controller chip. The signals were nice and clean within the constraints of my test setup. I saw three bursts of eight clocks that represent the three bytes being exchanged with the touch controller. 

I monitored the MOSI line and verified that it was transmitting what I intended. The first byte was 0b10000100 which asks for the temperature as a single ended measurement returning a 12 bit quantity. 

I verified that the clock is low when idle (sclk of 0) and that the incoming data on MOSI is held steady to latch it on the rising edge of the clock (spha of 0). The mode is what I intended and matches the diagram in the data sheet. 

Unfortunately, regardless of what command is transmitted, the response is always zeroes. 

Here is the oscilloscope trace of a transaction, with DCLK(SCLK) in yellow, DIN(MOSI) in green and DOUT(MISO) in purple. 

CS (Touch Select) isn't shown on the screen but when I enable the blue trace for it, I verify it goes low right when MISO drops to low. I removed it to have less clutter on the screen. The only difference with the representational chart they provided is that the time between the 8th clock of the first byte and the 1st clock of the second byte is wider than one clock interval. The chart seems to have that spaced wider as well, but with no numbers or other specifications I have no idea whether this is significant. 


I think I may grab an Arduino from my shop, wire it up and program it to drive the touch controller with commands like temperature sample or X position sample. I can then vary parameters until I can get the darned thing to behave as it should. 

Tuesday, June 20, 2023

Still can't get an answer back from the touch controller chip


I carefully compared my code communicating to the touch controller with what was working properly talking to the LCD controller. I looked for flaws, I checked that I had the correct pin assignments inside the FPGA. I even slowed down the SPI link further to just over 1.5MHz in case there was a problem with the higher speed. 

There are varied claims by the vendors of the LCD Module, its documentation and even in the sample library from Waveshare they distributed. Right where it does the access for the touch controller, however, it is setting the SPI link to 3MHz for those communications and then back to 18MHz for transmissions to the LCD controller. Just in case the timing is critical I will again change the slow speed of my SPI to a 3+MHz. 

Putting the oscilloscope on the SCLK, MOSI, MISO and pseudo SS (Touch Select) wires let me watch what we sent to the touch controller and what came back. That can be compared to the timing diagram from the XPT2046 data sheet. 

The MISO line, the output from the XPT2046, never budged from 0. I sent a different command asking the controller to send me the value of the temperature in the chip, but it also returned 0. I could see the Touch Select (chip select) go low for my transaction, the clock enter the board and the MOSI line send the command byte plus two bytes of 0 as required to get back an answer. 

I switched LCD Modules, same result. I disconnected the MISO line from the FPGA to eliminate the chance that the FPGA is dragging down the line. Still nothing coming out other than continual zeroes. 

I have to believe that this is a timing issue of some sort, where the touch controller chip is not triggered properly to deliver output on the MISO line. I started looking at the original sample code before I had ported it, since this may be a porting error on my part. I see that the code does a pause of 200 microseconds between the first byte transmitted (command word) and the next two bytes of 0 which allow the chip to send back the measurement. 

The way this is implemented is through a highly unportable, crude method of running in a loop 200 times to burn off the amount of time that a Raspberry Pi Pico would consume. I dropped it to 1 microsecond but that is clearly too long if you look at the scope trace. 

Eventually I removed the delay and pursued all three bytes by pushing them into the FIFO, waiting until the last one was complete and then pulling three bytes out of the receive FIFO. No change although my scope trace looks very much like the amateurish timing diagram in the data sheet - I say this because there are no durations listed and instead it is kind of a representational picture not drawn to any scale, ergo pretty useless for engineering against. 

My last stab was slowing this way, way down to 125KHz SPI rate, hoping that if it is slow enough I should see MISO modulating with captured values. I did not. At this point I just don't understand what must be done to drive that darned touch controller to return a non-zero output. 

As a complete fallback, I could install three physical buttons for the up, down and select operations, ignore the touch functionality entirely, and complete my user interface. That might not be the worst thing to do since I still don't know what accuracy and repeatability, if any, I can expect from the resistive touch panel. 

I haven't totally given up yet but this is quite perplexing. I see the signals going in on the wires just as they should, matching the requirements as described in the datasheet, but nothing is happening. It acts as if it is dead. Two different LCD Modules act the same. 

Testing the touch screen interface and dual speed SPI logic


My code now has functions to set the SPI link into two different clock rates, since the touch controller needs a relatively slow 2.5MHz while the LCD controller needs to be much faster to eliminate objectionable delay redrawing screens. I am using 25MHz for LCD communications.

In order to change the speed (baud rate), one simply has to disable the link, store a new value in the baud rate register and then enable the link for operation at the new speed. The baud rate is generated by dividing the 200MHz internal clock by the divisor we stored in the register. In my case, the value 8 which causes the SPI to operate at 25MHz. Storing the value 80 will instead give us the 2.5MHz rate.

To minimize the number of variables being changed, my first test will simply slow down SPI and redraw the LCD screen at the slower rate as well as at full speed. By visual comparison I can verify that the SPI link is operating properly at both slow and fast speeds and able to switch without a hiccup.

This worked perfectly, with a clear difference in the speed of clearing the screen to a different color. I started fast with cyan, slowed down and drew red which was noticeably slower, then sped back up and drew blue which was again fast. 


The touch controller raises an interrupt request line whenever the stylus or a finger is touched to the surface. I am not using this to cause actual interrupts in the Linux system, but using the PIO function in my FPGA logic to see if a touch has and is occurring. PIO will set a bit to show whether an event has ever happened since the last reset of that detector. 

Thus I can read from the memory location tied to the PIO to tell if a touch was done, validate that it is still active by looking at the current status of that wire, and take appropriate action. That action would involve communicating with the touch controller over SPI at slow speed asking it to reach the location of the touch. My first test is simply to see that we do detect touches by reading the PIO registers. 

The first test, once I resolved an oopsie with pointer arithmetic, did successfully register a touch when I first tapped with the stylus. Unfortunately, it kept registering a touch and interrupting even when I had removed the stylus. That too was caused by a small error in my program, which  I corrected rapidly.

I am seeing the touch signals immediately as well as the latched in falling edge. Due to some bounce I did sometimes get a spurious additional edge captured. If the stylus is held in place, we get one edge initially but then only see the continued touch state. I think that my best bet is to look for both edge detection and a current touch to trigger a request to the touch controller for coordinates. 


When a touch is taking place, the touch controller can take a voltage reading from both the X and Y resistor networks on the touch panel. We request this with a message over SPI. The library from Waveshare that was provided with the LCD Module reads these values several times to get an average position. It is up to my code to then use that reading to assign the touch to one of the arrows or the button if the user is requesting an action. 

This part of the testing only verifies that the touch controller responds with some location information while I am touching the panel and interrogating the controller over SPI. It doesn't yet tell me if the data is valid or useful but does prove I can talk to the controller chip.

This is failing. I seem to get nothing back but bytes of all zero. There can be many reasons for this which I need to start researching. 

Monday, June 19, 2023

Drawing on the LCD screen to test functions I need - part 10


I stopped fooling around with 'user friendly' programs that won't let me control all the parameters of the bitmap being generated. GIMP to the rescue where I was able to rebuild my arrows, at the desired smaller size, with the added benefit that I could recolor it cyan to better match the selection toggle button. 

The relative placement of the two arrows and the toggle button were tweaked until I was happy with the interface. The starting point before the failed attempt to shrink the arrows is shown below, along with the UI operating after the bitmaps were mangled. 

UI with oversize arrow bitmaps

UI with mangled bitmaps

After my work with GIMP and a test, I did some adjustments to end up with an acceptable user interface screen. The UI shows 13 file names of a larger number on the SD card, with the first entry highlighted but not selected. 


I tried to bump up the speed of the SPI link to the LCD controller chip since the documentation for the module asserted that it had been tested up to 60MHz. When I set it at 50MHz in my setup, it failed. This may be due to poor signal quality since I was using cheap jumpers. In any case, I switched back to 25MHz which seems to work reliably for the screen. 

Dual speed SPI link needed


The process of redrawing the screen sends a substantial number of bits over the SPI link. For example, clearing the screen by writing black pixels sends 2,457,600 data bits over the link so that at 25MHz this operation alone takes about a tenth of a second. At the earlier 2.5MHz testing speed the pause was painful. Each time an up or down arrow is pushed there will be a redraw. At least now it will be tolerable. 


The touch controller chip (XPT2046) is accessed at 3MHz in the demonstration code, which drops down from the 18MHz speed it uses when communicating with the LCD controller chip. My nearest attainable speed is 2.5MHz, in order that the touch controller chip can communicate with us. Per the top section, this is painfully slow when drawing screens through the LCD controller chip. 


The general logic of my interface is that once we through up a screen, there is no action on the LCD screen until the user has touched somewhere to attempt a button press, or the disk drive in the IBM 1130 has come up to speed and believed it loaded the heads onto the disk platter. 

Thus the user interface logic is watching for the touch interrupt, the signal for head loading, or the signal that our power has failed necessitating a quick unload of the disk contents to the SD card. When we enter this watching portion of the logic, we will call a function to set the SPI speed down to 2.5MHz for communication with the touch controller. 

After we see a touch, we will have interrogated the controller chip to extract the X and Y coordinates of the touch action. These cause various actions such as moving the highlighted virtual cartridge name, ending with the need to redraw the screen. To redraw, we first will call a function to set the SPI speed back up to 25MHz. 

Changing the speed is straightforward. We disable the SPI link by setting a bit in a control register, we load the new baud rate value in another control register, then enable by restoring the bit in the first control register. These will be simple functions contained in a new changeSPIspeed module, called SPIslowdown and SPIspeedup respectively. I am developing the code now and will add the calls into the user interface module as described above. 

Drawing on the LCD screen to test functions I need - part 9


I modified my code to set up the top 480 x 240 to display 13 file names from the extracted list built by reading through the CARTS directory on the SD card. I chose black as the background color with white for most text entries. 

The currently highlighted entry, which is moved by the up and down arrow buttons, has a gray background with white text. If the select button is pushed to toggle on selection of the currently highlighted entry, then the background becomes cyan and the text is red to show that this virtual cartridge is now loaded into the disk drive for access.

By up and down arrows are bitmaps in bright green and the select button is a rectangle in the center, which is cyan to match how the selected entry appears in the list of cartridge names.  


My bitmap of the up arrow is displaying successfully. I realized that I wanted the background outside the arrow to be black, which is my preferred background for the user interface. The relatively small font shows well as white over a black background. 

I do have one anomaly where the down arrow is shown but appears pointing up instead - I will debug this although it was in test code and not the code that will draw the user interface.

The current size, at 130x130 pixels, is just too big for a 480x320 screen. I plan to shrink these to 80x80 before I make use of them. My first try was unfortunately done with Microsoft tools. When I selected the choice to resize and chose 80x80, it unilaterally converted to 32 bit color values - sigh. Another tool changed it back to 24 but somehow the image is completely screwed up now. I guess I need to start over creating the arrow bitmaps. 

Drawing on the LCD screen to test functions I need - part 8


The 8 point and 12 point fonts are far too small on the screen. If I go with the 16 point font, I can still get 40 characters across the screen from left to right which is an acceptable tradeoff between visibility and useful length of the name. I am fine tuning the user interface around this font size. 


The sequence of initialization commands sent by the demonstration library (from Waveshare provided with the LCD Module) includes two interesting ones that are the source of my issues. The memory access sequence is set by:

Command     0x36 Memory access control

Parameter    0x28 MV - row column exchange and BGR - reversed color order

This swaps X and Y, thus my carefully designed coordinates are instead oriented the wrong way.  Ichanged this parameter to add MX as well, which works properly. The color order matches how we transmit the 16 bit pixel color value, 5 bits blue, 6 bits green and 5 bits of red intensity. 

I also note that this command is issued to invert the pixel color when displayed from memory. 

Command    0x21    Display Inversion On - pixel values reversed

I don't understand why this is working but I get satisfactory results so I will leave it in for now. 


My arrow bitmaps appear to use some scan method that produces an odd output. The comment blocks in the Waveshare library state that bitmaps must be done with progressive scan. I guess I have some more reading to do in order to understand and then correct my bitmaps (or the display program). 


I will use the entire 480 pixel width of the screen to display each file name, thus the arrows and button must be down at the bottom of the screen. With a height of 16 pixels per character for the font I am using, I can no longer fit 20 names per screen as I originally chose. 

The screen is 320 pixels high and I need to reserve space to display the arrow bitmaps and the select button. My current bitmaps are 80 pixels in height, which leaves me 240 pixels for file names. Adding in a little margin, I decided upon 13 files per screen. 

This requires changes to the code that draws the screen as well as the code that implements the up and down arrow movements. I am parameterizing all this as I go along, pulling those choices up into the project header file as defined constants. 

Sunday, June 18, 2023

Drawing on the LCD screen to test functions I need - part 7


As a quick test I wired one of my new LCD Modules instead of the original one, simply to eliminate the chance that some hardware failure on the board was causing the problem. The behavior was identical. That does point me back at my DE10-Nano either in the Verilog for the FPGA side or the C code on the Linux HPS side. 

Up next was a more complete oscilloscope examination of the signal lines. Previously I had monitored the three SPI function output signals (SCLK, MOSI and SS) as well as my software driven pseudo Slave Select for the LCD module. Those worked properly.

Other signals essential to the operation of the LCD Module are LCD Reset, LCD Data/Command Mode, and Touch Select. First up was the reset since a faulty reset, held low, would keep the LCD controller chip from responding. A clean initial reset and then steady unasserted (high) levels were seen. Next was the LCD Data/Command Mode which when low tells the controller chip that a command is being sent, while high the controller expects parameters or other data. 

That signal was frozen in the high position. It never emitted a low state, which it must in order for the controller to reset, turn on and accept the other commands I was sending. That output would completely explain why the screen remained dark. 

This line is controlled by writing bits to the memory block in Linux that is transmitted to the FPGA's PIO output module, that in turn outputs the bit on the external I/O pin. This same method is used to send the reset command, which worked, and to send the LCD Select pseudo-SS which worked, thus the issue must be some subtle flaw. 

I did find it! There was a defined constant in my code that was wrong, thus I wasn't toggling the LCD Data/Command Mode bit when I thought I was. This was fixed in a trice and the screen came to life. I discovered a few issues with the user interface but can write what I want on the screen which is what is important.


The first thing I attempted was to write a text string to the X and Y address 0,0 in order to see whether the letters are cut off at the edges. The text was sideways starting from the bottom left corner, not in the orientation I expected. 

The second observation was that 8 point font is very tiny indeed which will be hard for a user to read when they are selecting virtual cartridge file names. I will need to increase the size, which limits the number of file names on the screen as well as the maximum length of a name I can display. 

There was an error in how I used the GUI function to write the strings, which I attempted to use to list the file names found on the SD card. I received out of range errors, which I think means that I have to set the window boundaries properly or pass proper parameters. Same with the arrow bitmaps. 

Finally I drew a rectangle on the screen. I believe I have the position and orientation wrong, similar to how the text was misoriented with the earliest strings I wrote. 


I will chase down the reason for the strange orientation of the objects on the screen, then correct it. That should cause my text to run from left to right, with the lines going from top down to bottom. That should also put the rectangle in a better place.

I will experiment with larger fonts, finding the smallest that is acceptable and then sorting out the number of lines and max name size based on that. This will change a fair amount of the user interface code. 

Friday, June 16, 2023

Reorganization of signals to improve cabling signal integrity


The DE10-Nano general purpose IO connectors are 2.54mm 2 x 20 connectors, very like PC disk and floppy connectors, using ribbon cable to carry signals between the ends. I will use the ribbon cables to bring the signals over to the other two boards but they will not work plugging in an identical connector so I have some wiring to accomplish. 

The LCD Module end does not use the same connector, instead it has a space designed to fit a Raspberry Pi Pico. That is similar to a Dual Inline Package but they separated the two rows of 20 pins by an additional 2.54mm thus incompatible with a DIP socket. 

My interface board does use the same 2.54mm 2 x 20 connector however my pin assignments vary. I also use the cable to carry some voltages which I don't want to connect to the DE10-Nano. This means I will be modifying the connections from the cable slightly. Same basic order of the signals, just a few places where the cable is split to skip over pins or slightly realign. 

KiCAD view of the board design


The goal is to minimize signals coupling (being induced in adjacent wires). With ribbon cables it is common to have a ground wire between each signal wire to accomplish this. I thus reassigned the signals on the DE10-Nano connector to allow the signals to be sandwiched between grounds. 


I have signals that connect this product to the IBM 1130 disk drive. Other wires are hooked to the LCD Module to support the user interface with an LCD panel and a touch screen atop it. For simplicity of cabling and routing of wires, I separated these to their own cables. The DE10-Nano has two GPIO connectors, thus I used GPIO 0 for the 1130 related signals and GPIO 1 for the LCD Module wiring. 


My earlier approach to building this product used an Arduino Mega with an LCD daughter card for the user interface and the SD Card where the virtual card images resided, communicating via SPI to a Xilinx S7 based FPGA board, but the toolchain was causing me major grief. That was when I changed horses to the Altera/Intel based DE10-Nano and its more stable toolchain. 

However, the Arduino Mega 2560 board used 5V signaling levels, while the FPGA board was based on 3.3V levels and the IBM 1130 system's SLT is a 3V scheme. To convert voltage levels for signals passing between the boards I developed that interface board which handled 3V, 3.3V and 5V signals. For example, a signal between Arduino and FPGA would convert 5V to 3.3V or vice versa. 

My DE10-Nano and the LCD Module are all based on 3.3V, which changes the voltage levels involved in certain signals. I looked over my board design and schematic and came to the realization that my only change is to connect the power pin for 5V to the 3.3V source, which would cause all the converters to properly shift levels between 3V and 3.3V as needed. 


I expect to build and install a few more of the Virtual 2315 Cartridge Facility products on other IBM 1130 systems. Currently another museum is planning to have me restore their 1130 to operation and its usability will be greatly enhanced by adding one of these to it. The National Museum of Computing in the UK has a running IBM 1130 which could make use of one of these as well.

I picked up another DE10-Nano and some LCD Modules so that I have the key components to build more of these systems. 

Sunday, June 11, 2023

Drawing on the LCD screen to test functions I need - part 6


I found a simple error in my porting changes to the function that should have been initializing the screen dimensions. It is now corrected and the previous error situations in routines to clear screens or draw objects have been rectified. 


While I feel that the code is correctly sending commands, that is 8 bit transmissions with the proper control signal activated, I have not fully checked how this is handling data which is either a single frame of 16 bits bounded by my LCD Select or multiple 16 bit words sent under a single LCD Select session. If either of these is not operating correctly then it won't initialize properly. 

I did monitor such sequences and was satisfied that the LCD Select, my falsified slave select to the LCD Module, is working as intended to clock in 8 or 16 bits, triggering the LCD controller chip to accept the command or data, as well as toggling as each 16 bit word arrives in a stream of them. 


The LCD controller has a memory that is 480 x 320 pixels in size, each pixel being a 16 bit color quantity. The controller can scan through the memory in multiple ways. It can advance horizontally along a line from left to right, then drop down to the next line which again runs left to right. This is called L2F U2D mode. There are modes that scan right to left, modes that bottom to top, and one can instead scan down or up columns first, then advance horizontally to the next column either right or left. Many combinations of modes exist. 

Commands configure the mode of the controller chip. For my purposes I will always operate in the L2R U2D mode. The method of transmitting pixels to the screen in any mode involves starting or restarting a write to memory using commands 0x2C (and 0x3C). Every data word following that command up until the next command byte is received will be entered into the memory, left to right rows moving from top to bottom. 

The decision for when the right end of a line is reached, so that the next pixel is put in the leftmost position one row down, is based on a window that is set up by commands. The command 0x2A defines the leftmost and rightmost coordinate of the window. If that is set to 0 and 479, for example, then when we stream in pixels they will start at the left edge of the screen and continue all the way to the right edge. If we instead issue 0x2A with an extent of 100 and 199, then we stream pixels in which are placed starting at X position 100, continuing to X position 199, then back to X position 100 on Y position + 1. 

Similarly, the command 0x2B will set the top and bottom coordinates of the window. If set to 0 and 319, then when we start streaming pixel data it begins on the top edge of the screen and after reaching the right end of a line, drops down 1 line until finally reaching the bottom edge. If we instead issue 0x2B with an extent of 50 and 59, then our first pixel is on the left edge of the window at Y coordinate 50 and once the pixels reach the right edge of the window, we drop down to Y coordinate 51 and eventually get down to the right edge at Y coordinate 59. 

Routines that draw objects make use of these commands - we set a window where we want to draw an object and then stream in the number of pixels needed to fill that window. They can be pixels of a common value, in order to clear an area to a uniform color. They can be uniform color pixels to draw a rectangle or square inside a larger area. They can be areas where patterns from the pixels form arcs, circles, lines and even font characters. 


There are commands to put the controller to sleep (power conserving mode) and bring it out of sleep. Commands turn on the display or turn it off. Commands can flip on all pixels or flip off all pixels. Commands can set the display into inverse mode - i.e a font character can look like black ink on white paper or like white phosphors on a black (or green) screen. 

I will be experimenting with these commands as they are simple sequences that should produce visible results on the monitor to quickly confirm my understanding. That will take place next. 

Drawing on the LCD screen to test functions I need - part 5


After some more changes, such as removing the device from the device tree entry for spim1, I decided to revert back to using the FPGA routing for spim0 since that had some effects initially. This time I routed the signals out of the GPIO pins directly without buffers, to avoid the timing problem I suspected would occur because of the buffers I had been inserting.

I had also created a diagnostic monitoring function to show the state of all the relevant SPI registers. This showed me that I had the SPI capability properly configured and the status was good. I then hooked up the scope to the new 

The loop I wrote showed me good activity on the clock, MOSI, MISO and hardware slave select lines. I hooked up the LCD Module but still had nothing visible. I did notice that the SPI link is operating at 5MHz rather than the 2.5MHz rate I thought I was configuring. I corrected the constants in my initialization routine and corrected this minor flaw. 


What I saw on the scope confirmed that the bits on MOSI changed at the correct clock phase, that the clock polarity is correct and that the bits I see are in fact the data I am transmitting. The most significant bit is transmitted first in every byte. Since the library and my code handle the two bytes of a 16 bit word explicitly there is no issue with the little-endian nature of the ARM processor as I send the high byte first. 


I produce four signals that are important for communicating with the LCD controller. LCD Reset is used to produce the desired initial state when we start up the program. LCD Command/Data tells the controller chip whether we are transmitting an 8 bit command or 16 bit data/parameter words. LCD Select is used to enable the logic to receive our SPI communications and transfer it in parallel to the controller chip. Touch Select is used to instead enable communications with the touch screen controller chip. 

These all work as intended. I can see a transaction bounded by my LCD Select signal, with the actual SPI link sending 8 bits at a time bounded by the hardware slave select. My LCD Select governs however to comply with the wacky protocol this device uses. 

I send MSB first so a xC2 goes out as 1 1 0 0 0 0 1 0 and these get shifted into the first SIPO shift register such that D7 is a 1 and D0 is a 0. The LCD controller chip is wired so that our data is appearing in the lower half of its 16 bit parallel input with D0 being the least significant bit. The LCD controller defines commands such that the 0xC2, Power Control 3, is represented with a 1 at D7 and a 0 at D0 which again confirms we are transmitting the bytes exactly as the controller chip wants to see them. 

As I deassert my LCD Select signal after the transmission of the one byte 0xC2, this triggers the LCD controller chip to latch this in and read it as a command, while resetting the logic outside to receive subsequent bytes or words. Again everything appears correct to be communicating with the LCD controller and sending the proper sequence to initialize it


I did discover some logical issues concerning what I need to do in order to use the LCD library provided by Waveshare which I have heavily modified in order to port it to the DE10-Nano. When I try to clear the screen to a fixed color I selected, nothing was transmitted over SPI. After instrumenting the library routines, I see that it does a check on the input parameters and skips clearing because the start and end points are all the same. 

It appears that I don't have some important initialization done for the screen data structure as it has the end points of the X and Y range as 0 instead of their correct 479 and 319 for the full extend of the screen. I will chase down where this should have been set and make sure that happens. 

Similarly, my tests writing fixed strings on the screen are failing because of this issue with screen data structure initialization. I may also need to issue some commands to set the current cursor or writing point before I start - this is an area I don't fully understand. 

Saturday, June 10, 2023

Drawing on the LCD screen to test functions I need - part 4


There was a bit of delay working out how to get the scope probes on the jumpers, since both connectors where the jumpers plug in are tightly spaced. Plugging in my 2x7 cable for .5mm pins at 2mm spacing, I yanked the other connector off and gained access to the wire ends. 

Pin 4 is the SPI clock signal, pin 7 is the SPI MOSI signal and pin 5 accepts the SPI MISO signal. SPI Slave Select sits on pin 6 and will be useful for triggering although the actual slave select going to the LCD module comes from the FPGA side GPIO pins and not the SPI modules slave select logic. I had to do this to deal with the kludge protocol required to drive this LCD Module. 

I saw no signal activity at all. Nothing. The SPI module is not working or not being routed as it should out through the LTC pins. My program was supposedly looping writing command bytes to the module which should have toggled slave select, drove the clock, and output the bit pattern I wrote. 


My first thought was that it was the damnable poorly documented routing mechanisms for the Cyclone V and the Quartus toolchain. I went back to the tool and opened the pin planner. To my surprise, I found that the SPI signals were NOT connected to the Cyclone V chip in spite of the Platform Designer and Quartus statements directing this to happen.

I look at the table and noticed that the pins listed for the SPI master in pin planner were different from the table in the user manual. I then opened my master spreadsheet of all the signals and routing for the Cyclone V chip to dig into it. To my surprise, I discovered that the SPI master connected to the LTC connector was not SPIM0 as I expected and configured - the primary SPI controller - but SPIM1 a second controller. 

As you can see, neither the table nor the diagram that sits above it has bothered to distinguish which of the two SPI masters is involved. The signal name HPS_SPIM_MOSI is not a real signal name, there are instead HPS_SPIM0_MOSI and HPS_SPIM1_MOSI and had some lazy so and so bothered to type the number I would have saved quite a bit of time digging into this. 

This is why I had some activity when my earlier design had routed the SPIM0 signals out to the FPGA and to GPIO pins. That method required me to introduce IO Buffer blocks which introduced delays in the signals, which was the impetus for me to switch over to the LTC connections instead.

I can change routing to get the SPIM1 routed to the LTC pins, replacing SPIM0. I would also need to change the offset I use to access the SPI control registers in memory, as SPIM1 is mapped to a different range of addresses. 

The remaining potential issue is that there is a Linux driver enabled for SPIM1 according to the device tree blob file. I used the dtc compiler to access this, vi to change spim1 to disabled, the compiler to format as a blob and was able get Linux to boot properly.


Still no signals showing up on the oscilloscope while the program is looping writing to the SPI channel. I realize that the device tree had listed a specific peripheral using SPI as attached to SPIM1 which may cause the driver to still interfere. My next change will be removal of this device mention in the device tree. 

For diagnosis, I began to litter the code with diagnostic printf statements so that I can validate every address being used. I also accessed and displayed lots of values to demonstrate that I have done what I expected to accomplish. 

Musings on the LCD Module I am using - missing capability


This is the module I bought and it is serving my needs adequately although it is missing the ability to read anything from the LCD controller chip which would have been handy in debugging and experimentation.


As I have mentioned in some detail before, this particular module has introduced an odd duck serial interface, not quite SPI, not I2C, but one that can be driven by (some) SPI implementations on microcontrollers and microprocessors. The LCD controller supports serial interfaces but the designer of this module chose to configure it in 16 bit parallel mode, then convert incoming serial to parallel with a hodgepodge of TTL chips. 

Anyone think I have used enough pejoratives describing this? Hodgepodge. Hillbilly. Odd Duck. Oddball.  Previously I did characterize it as Rube Goldberg-ish but really it is not that sophisticated an kludge. Enough already!

This clocks in serial data from the SPI MOSI line into a 16 bit latching SIPO shift register. It is able to handle frame (word) sizes of both 8 bit and 16 bit. It introduces rules for the SPI Slave Select line that help drive the LCD controller chip but are outside of SPI protocols. 

Most seriously, since it is not really SPI, it has no means of sending data back from the LCD controller to the host over the SPI MISO line. This is unfortunate because the LCD controller has a rich set of commands that return internal state, configuration information and memory contents, all of which are unavailable. 

The parallel interface of the controller chip would place the outgoing data on the 16 signal lines, but there are no TTL chips or wiring to receive them. The existing shift register could make use of its output enable pin to stop driving the parallel outputs, thus allowing the tristate LCD controller signals to output during a read. 

Read and write operations on the 16 parallel pins of the LCD controller are effected by rising edge signals on the write enable (WRX) and read enable (RDX) lines. The TTL assemblage that is converting serial to parallel does toggle the WRX line to cause it to capture the data or commands coming from the host. The RDX line is hard wired to 3.3V and thus never has an edge to drive a read output. 


One could imagine that additional circuitry could be added with a second board and the RDX line redirected from 3.3V to this new circuitry. The challenge is that the parallel interface and the command structure of the LCD controller would require a sophisticated state machine to determine when to strobe the RDX line, based on what commands had been transmitted and when those would be returning data. 

For example, the command 0x09 will read out the status of the LCD controller and screen. One would write the 8 bit command to the device, then write up to four 16 bit words whose value doesn't matter because we are capturing the returning data from the MISO line. 

The nature of SPI is that we are simultaneously sending the bits from host to controller (MOSI) and from controller to host (MISO), like ships passing in the night. Thus one cannot respond over MISO to something that is coming over MOSI since you will not have seen the MOSI data yet, all responses need to be sent over subsequent transfers. This command sends x09 and ignores MISO, then sends four more irrelevant transfers over MOSI while watching for returns from MISO.

A peculiarity of the controller chip means that for many read type commands the data is not yet ready to return when the second transfer begins (the first data transfer after the command is sent). Thus we are essentially doing a dummy exchange of MOSI and MISO as the first data transfer. The second and subsequent data transfers is when the MISO information is being returned. 

My state machine would need to read every command sent, determine what the pattern of returned data will be, then block the WRX on the data transfers and instead toggle RDX to latch in the returning data. If done properly, a second (PISO) shift register could clock out that data on the MISO line fulfilling the expected behavior. 

Note that the 8 bit command and values are part of the 16 bit parallel interface, we just ignore the high order 8 bits. The wacky protocol they devised transmits a short 8 bit word rather than a 16 bit word with zeroes at the top, when sending commands, but sends the full 16 bits when transmitting data. 

This is far too much effort for little practical benefit. I will not be attempting it, but I burned the brain timing musing over it and decided to share it just in case this helps someone with a similar design issue. 

Thursday, June 8, 2023

Debugging the small BMP display routine - used for up and down arrows


I don't yet have the SPI link to the actual LCD Module debugged, but I built a mainline routine that would expose the same library calls as exist in the actual library, so that my code to display the BMP could execute. I would intercept each library call and show diagnostic information about that invocation. 

The three call types that are used by my code are LCD_Clear, LCD_SetWindow and LCD_WriteData which set the display to a uniform background, establish the size of the window within the screen for our output, and outputs each pixel. The scan direction is set up so that pixels are placed Left to Right in a line, then Up to Down for successive lines - L2R_U2D mode in the library. 

In an early version of the program I output the 16 bit hex value for each pixel - our display module uses a cut down color space with 5 blue, 6 green and 5 red bits squeezed into a 16 bit value. This chewed up a lot of space on the terminal with almost 17,000 lines output to represent the bitmap image. 


My code rejected the initial BMP file because it was not a 24 bit color depth. I realized that the MS Paint application, which offers only the options of 'black and white' or 'color', was generating 32 bit color depth. I found an online converter that would make the bitmap 24 bit, repaired my files and was then able to work with them using my code. 


My code displayed the calls to narrow down the window for the arrow to a specific 130 x 130 area starting at x value 300 and y value 40, representative of what my user interface will do. It then counts each time we enter with a 16 bit word that sets the color of one pixel in the window. At the end we see that the count was 16,900 and we called to reset the drawing window back to the entire 480 x 320 screen area. 

I may have an issue if the bitmap file is ordered from bottom row to top or some other way compared to my L2R_U2D orientation, but if that is so I can either modify the bitmap or call the library routine to switch the screen mode to whatever is appropriate. This will have to wait until I see the bitmap on the actual LCD screen.