BOTTOMS UP CREATION MODULARLY
It is easier to grasp and debug functionality if it can be logically grouped and divided into reasonably compact VHDL files. I chose to create such modules to handle each of the three bridges between the Hard Processor System (HPS) and Field Programmable Gate Array (FPGA) sides of the System on a Chip (SoC) - HPS to FPGA, FPGA to SDRAM, and Lightweight HPS to FPGA.
Thus I built the functions getdata, getsdram and getcommand which handle those three bridges respectively. Each controls the signals to the bridge interface, using the Avalon Memory Mapped protocol. The first name of each bridge indicates which side is the controlling element (master) with the other side its vassal.
One level up, I wrote a module loopcart that would perform the entire process to load or unload a virtual 2315 cartridge into the SDRAM. A cartridge is organized as 521, 304 words of 16 bits each, which are grouped into 321 words per sector, eight sectors per cylinder and 203 cylinders for the entire cartridge. This requires just under 1Mbyte of SDRAM which I assigned to the tail end of the 1GB of SDRAM installed on my Terasic DE10-Nano board.
When a user selects a particular file on the SD Card attached to the HPS side, the user interface program will send a load command over the Lightweight HPS to FPGA bridge. That command kicks off the mechanism which will accept the stream of 521, 304 words and write them onto SDRAM in the final 1MB.
Our HPS 2 FPGA and FPGA 2 SDRAM bridges transfer 64 bits at a time - four words - thus we actually need only 130, 326 writes from the HPS side to transfer the entire cartridge image down to the FPGA. My loop module starts at the beginning of the 1MB zone on SDRAM, then waits for the HPS side to write all those blocks of four words sequentially. As each block arrives, we write it to the SDRAM and advance our address.
The FPGA side has no control over when a block of four words will be shoved at us by the HPS side, thus we depend on a pacing signal waitrequest for the HPS 2 FPGA bridge. Turning on waitrequest tells the master side of the bridge that we cannot accept its write request so it remains waiting patiently until we temporarily turn off the signal to let the write complete.
We need this because the process of writing the block to SDRAM might not proceed as fast as the HPS side can attempt to write groups of four words. In that case, the master side is held waiting until we have completed the write to SDRAM and can accept the next block. A status word is emitted when the loop module has completed the entire cartridge worth of transfers, which then can be read by the Lightweight HPS to FPGA bridge as a confirmation we have the virtual cartridge 'loaded' onto the disk drive.
After the IBM 1130 has made use of that virtual cartridge by reading and writing, eventually the user flips off the power switch to unload the disk drive. We see this and begin an unload operation, again using our loop module.
The unload begins at the start of the last 1MB of SDRAM, fetching blocks of four words, then dropping waitrequest so that the read from the HPS side can complete. The HPS side reads 130, 326 blocks of four words to fetch back the entire virtual cartridge file into the Linux application. We write this updated file back to SD Card at the end of this unload operation.
SIMULATING MODULE BY MODULE
I set up some testbench code for each module and developed suitable signals and timings to validate the operation of each of the modules - getdata, getsdram and getcommand. Varying times and conditions let me prove out how they performed against my understanding of the Avalon MM protocols. Later of course this must be proven to work properly on actual hardware.
Here is the simulation of getsdram under various read and write requests, also testing the waitrequest functionality. This module is a master type interface and the vassal side over in HPS will use waitrequest when it is unable to accept another read or write because SDRAM is busy.