I triple checked all the UCF file pin assignments and all the hookups from the memory controller and USB logic, but it all looks right. I did another experiment, where I loaded the RAM using my logic then forced the standalone ADEPT memory to retrieve what was in the chips. That would tell me if I was writing the contents but not able to read properly, or if I am not even writing.
The testing was illuminating. First, I used my logic and the Adept File IO utility to load RAM with a known pattern, then used the Adept Memory (standalone) logic to extract that RAM to a file. The data was random characters, not what I wrote at all.
Next, I used the Memory (standalone) function to load RAM with my known pattern, reset the FPGA to invoke my logic, and tried a read operation using the Register mode. The data matched!. That means that I am correctly reading the RAM, but my logic doesn't write it correctly.
Writing to RAM involves an area where I made changes to overcome the Xilinx inability to correctly hook tristate from an internal module out to external pins. I am using a MUX to drop the high impedance (Z) state and pass through the intended data to write. I know that data is correctly formed because I see it on my LEDs. However, if the mux doesn't give proper setup and hold times for the bus, I could get random crap being written.
Turns out the problems are simpler and more localized. Two issues. First, crap documentation for the reference code led me seriously astray. It defines a bit in the config register, bit 0x10, as unimplemented and to indicate that the memory is in programming mode, not to be used by custom applications. Second, the File I/O to load memory consistently gives the same wrong data in RAM.
When I set the config register with bit 0x10 on, manually reading and writing, whether in single byte or word at a time mode, worked perfectly. Whatever I wrote to RAM will come back out when I read it. When I write it to RAM, I can then use File I/O to dump RAM to a file which comes out properly.
What does not work right is File I/O to load a PC file into RAM using this logic. It claims to have completed properly but I get the same gibberish in memory every time, regardless of what is in the file. I tried loading a different file entirely but got the exact same gibberish in memory.
This may be a flaw in the utility, not in my logic. The only consequence of this is that I can't just fire up the Adept utility and load RAM from a file - it will get the garbage loaded. I can use the utility to suck a copy of RAM up to a file, and can manually store location by location using the Register IO tab, but if I want to take a known cartridge image and load it into RAM, it will need to involve a program I write on a PC.
I moved back to coding, starting to code the next higher layer in functionality, an FSM that will turn off the incoming word stream (stop sync), wait a requested amount of time to skip over some preamble, then turn on the deserializer to sync up and deliver a target number of words into the buffer beginning at the indicated starting point.
I realized I had to build the logic to write to a buffer word before I would know what my read field FSM needs to do, so that became the highest priority task. I have two ways to go with this, leveraging the memory controller from Digilent I was using for USB access, or drive the memory myself.
Given the flawed behavior of the File I/O attempts to write, it was possible that the Digilent reference logic was defective and that would compromise my code, so I instead build a memory read/write FSM from scratch. The memory is a Micron Async/Page/Burst CellularRAM 1.5, part MT45W8MW16BGX. It is a DRAM that auto-refreshes and acts like an static ram chip.
I worked out a very conservative state machine that is sure to give me clean data in and out of RAM, operating at 200ns although the chip itself does its one access in 50ns - four states in the machine are what swell the time. This way I don't have to worry about this aspect of the design.
Then, when I looked more closely at how to have the Digilent logic and my FSM share the chip control lines, I saw that many of the control lines were set up as tristate buses to allow sharing.
However, that meant that the internal module with the digilent code was setting signals to Z when not active, which triggered the abysmal conversion to muxes by the Xilinx toolchain. These may be the reason for the bad behavior with File IO I had discovered. It is positively riddled with code that treats its inputs and outputs as tristate.
I should have realized when it was the only reference project that used a schematic view for the top level, rather than a top level module. I bet this avoids the mangling of the logic that occurs when a regular VHDL module is used as the top.
I decided that I would yank the whole memory controller logic from digilent up into the top level module, instead of trying to manage all those muxes. It was messy but it should resolve the problems and allow me to share the chip lines properly.
It will let me share lines for the RAM and it works as before, but exactly as before, meaning it still does not write properly using File I/O but does just great otherwise. I built my read/write memory FSM to use tristates on the same chip pins, meaning everyone treats it like a bus.
I am starting to build up enough logic here to warrant driving some of it with a testbench and validating function well before I hook this up to a real drive. That will definitely add time to the project but the up front time will be compensated by less debugging time later.
My read/write of memory is nicely integrated, although not yet tested. Time to get back to the read field function, which will pick up one of the three records or fields in an Alto sector. I reached the end of the evening while working on this state machine.
No comments:
Post a Comment