Debugging virtual 2501 card reader
Shortly after posting yesterday's entry, I discovered a flaw in my virtual adapter logic, in the fpga side. It was the new functionality I added to support timely Sense DSW reset and operations complete bit clearing, by moving that function down to the fpga while keeping the bulk of the device adapter logic up in the PC in the Python program.
It was necessary to have each interrogative command from the PC do a reset on the value it was retrieving, otherwise the PC side code can't tell whether a new XIO has been issued or we are still seeing the previous command. For example, a card reading program might issue a second XIO Initiate Read to get the second card, both of which have the same function code and buffer address. The only way to detect this is to reset the function code when the PC polls it, thus the PC will see zeroes until another XIO has loaded the function code again.
The flaw was in timing - since the interrogative commands Poll and Pollreset should receive the then current function and reset bits, but clear the bits before the next Poll/Pollreset comes in. I was clearing the stored so fast that what I sent to the PC was the newly cleared zeroes, not the value that was in the UCW at the time the transaction began. It took some thinking to find a way to guarantee clearing while also ensuring we transmitted the prior value. Once I worked that out, I changed the logic and had it ready for testing.
When I fired up the test, I did trigger a busy condition when I issued an XIO Init Read, but my adapter logic in the PC wasn't able to get to the point where it would fetch the function and WCA. Instead, I seemed to be stepping on results needed by the status polling routine that runs every second to update the 1130 state indicators on the GUI.
Since each interaction over the link is a strict transaction of two outbound words followed by two inbound response words, I am not sure how this is getting out of sync. The error I was seeing was a status interrogative from my Python program which was getting back the wrong value when it should have seen the 0x000C it just sent.
This required a new set of diagnostic outputs to figure out how it might be getting out of sync, but I also took a peek at the logic surrounding the card reader since this behavior was triggered by the fpga side of the virtual device.
My python program will not refresh the 1130 state while the virtual card reader is 'Ready'. Similarly, the virtual 2501 logic will stop polling if the card reader becomes not ready. Further diagnostic tracking showed that I was sending the Poll interrogative to the fpga and it was handling it, but the value returned was always '000' rather than the expected '110' after an XIO Initiate Read.
Diving down with more diagnostics, I found that I was not storing the function code in the UCW, so my instrumentation was updated to focus on this situation. I did see some suspect logic in the handling of the UCW update, where a one or two cycle hazard existed that would advance the state machine back to idle too early. I made the change as well as the diagnostic outputs then retested.
I am now correctly finding and resetting the XIO function code from the Poll interrogative, but when I retrieved the saved WCA (address of the table with card count and buffer), I got back zeroes not the address 0x1010 that I had coded in the instruction on the 1130.
A new set of instrumentation and some careful looking at the fpga to make sure I am indeed saving the WCA into the UCW as I should. Other possible errors were failure to process the pollwca interrogative or a failure to send the UCW field out as a response to the interrogative. I quickly found it was a problem saving the WCA, yet the logic seemed correct.
I looked into the module that processes the XIO Init Read instructions in lockstep to 1130 states and cycles. There, I found the cause of the problem. The XIO Init Read instruction has two execute cycles, E1 and E2, when it fetches the two words of the IOCC. In cycle E1, it latches in the function code and modifier bits. In cycle E2 it saves the address word, which is the WCA. I was setting the busy condition as soon as I latched some of the fields in E1, but that fired off other FSMs that updated the UCW including the WCA address that I hadn't received yet!
Moving the busy signal so that it fires only as we latch the WCA in E2 ensures that other FSMs triggered by this busy condition have all the data they need to store into the UCW. There was a parallel flaw in the XIO Init Write command logic which I corrected at the same time.
Back to testing where I then realized that I wasn't saving the XIO function code, this time because I was looking at the B register value when busy was triggered, but due to my changes to the Init Read and Init Write logic, I am now in E2 when the B register no longer has the function code present. Since I already know what code I am handling, with each module having a unique busy trigger (e.g. busyinitread versus busycontrol), I could just hard code the function to store into the UCW.
Changes made, I did the next test round. This time, I had the proper data in the UCW fields. Making the virtual 2501 card reader 'Ready' with a file of card images, I then issued the XIO Init Read and saw that I was getting the WCA, the count of card columns to 'read' into core, and was starting to store the first column when I encountered a Python issue.
I worked on the PC side program for a bit to fix up the issue I was getting, after which it was time to test once again. Now I was successfully storing the number of card columns requested in the WCA into the locations at WCA+1 and downward in core. The end operation status was put into the DSW and interrupt level 4 was properly triggered.
I tried XIO Sense DSW and XIO Sense DSW with reset but the interrupt level trigger remained on. This is probably a flaw in the fpga logic that shadows the DSW but it could be problems in handling the PollReset interrogative and its effects. Once again, time for diagnostic instrumentation and some testing.
I was fairly sure I had resolved the issue and went back to testing. One issue I observed is that the simulator format set up by Brian Knittel defines what characters are valid in card images, but real decks I have read in contain some additional characters. The original behavior of the Python program would reject a file that had these characters, but I changed the code to simply replace any 'invalid' character with a space.
I prepared some files to test the behavior for when the 'hopper empties', leaving a last card in the pre-read station. I should be able to open the next file and resume reading taking the pre-read station card and then the cards from the new file. Otherwise, if I mark it with the 'last card' function, the file should read that last card and mark the DSW appropriately.
I modified my test program code in the 1130 to support these tests. I discovered that my logic to flip the reader between 'ready' and 'not ready' was not working properly. That blocked any chance to completely prove out the last card condition, although it did seem to be setting the bit.
The problem is most likely entirely in the Python program side of things. As daylight ran out, I moved inside where I did some code review and thinking about how I was dealing with the emptying of the virtual hopper.
Building SPI serial links to remote fpga and microcontroller boards
I finished up the data pump for the high speed SPI link, making it a loopback of the data word so that my remote board can test the interface easily using buttons, switches, LEDs and other indicators. Once I write the test logic for the remote board, I can load it into one of my digilent Nexys2 boards and begin debugging.
The medium speed link is different, including a sequence of words that form transactions. The high speed interface just pumps a registered output word to the remote and registers the incoming stream to an input word. This just mirrors state. The medium speed link adds error correction and other meaning transactionally, by series of 8 bit words, not in parallel. That added a bit more logic for this link. I haven't finalized the protocol thus I left this to code later.
Tonight I built the testing function for the Nexys2 board that will pass switch and button signals up to the master fpga board which sends them back to select various lights and indicators. When it was ready I loaded it onto the board. This can be wired up to the main fpga board and tested tomorrow.