[print this page]

Memory Model for the multicycle CPU

At this point you should have finished your multi-cycle CPU (including state controller) design. You have also simulated the design for correct execution of each instruction (one-at-a-time).


1) Reconcile the interfaces between memory operations on the CPU with memory operation for the memory model. As you will see below, there are significant differences between the control signals, bus structure, word format,and timing of these two interfaces.

2) Model and simulate the entire system (memory and CPU) in the simulator.


When you are done, this is a block diagram of what your design will look like.

  • The CPU block is your multi-cycle CPU that was designed previously.
  • The Memoryblock is a model of actual memory hardware. You can find this block in the CSELib library as the component "memorymodel".
  • Memory Controller is a block that you will design to reconcile the timing and control signals between the two interfaces.
  • Alignment Unit is a block that you will design to reconcile the memory word format between the two interfaces.


    Lets take a detailed look at each of these units.



    Memory

    Memory serves as a storage space for your data and program. The Memory block shown in the above image is actually the MemoryModel component in the CSELib library. The symbol for this component is show below:


    The address size of the memory model is 24-bits. This address selects a 32-bit block of memory. When you add an instance of this componentto your design, you will notice a "generic" declaration attached with a pointer to a file. Using this generic, it is possible to initialize the memory contents for the memory model from a text file in your work directory. You will use this feature to load the memory with a test program to be executed. See the section below on how to prepare this program. Once prepared, modify the generic declaration that is included in the symbol file to point to the file that contains your program and recompile the design.



    Memory Controller

    You may have noticed that the signals at the pin-out of the MemoryModel and the pin-out of your CPU do not match. In fact, they differ significantly both in function and timing. The purpose of the MemoryController is to rectify this difference. It will be a state machine that accepts requests from the CPU for read or write operations, synchronizes the CPU with the memory using the MEMWait signal, and manages the interface to the memorymodel in order to make the transfer happen.


    To understand how this will work, lets begin with a description of the memory interface signals coming to and from your CPU. These are the signals shown to the right of the memory controller in the diagram below. These read and write transfers are the same whether your CPU is doing an instruction fetch, a load, or a store operation. (except for differences in the read/write controls)



    CPU to Memory Controller Interface

    Memory operations from the CPU proceed as follows. Refer to the timing diagram below.

    Clock cycle 1 - the requested memory address is placed on the MemoryAddress out. Either MemRead or MemWrite becomes active for a read or write operation respectively.

    Clock cycle 2 - the memory controller asserts MemWait back to the CPU The CPU now holds in a wait state until the first clock edge (Cycle n) after wait is de-asserted.

    Clock cycle n - The memory controller places read data on the data input bus and deasserts MemWait. The CPU deasserts MemRead and latches the data into the input buffer register at the end of this cycle to complete the read operation.

    (Note - a practical design choice would be to have buffers on the input to and the output from the CPU, however if you look at your CPU design you will see that these buffers are already in place.)




    Memory Controller to MEMORYMODEL INTERFACE

    To read data from the Memory Model, the first thing to do is to assert the MemBusReq_n low. The "_n" indicates that this input is active low, so a '0' will activate. The Memory Controller will then wait for the Memory Model to grant bus access via the MemBusGrant_n signal. At that moment, the Memory Controller should see that it has access and assert MemStrobe_n low, MemWriteSel_n high and the desired memory address to be read on MemAddr_OutReg. The following cycle, we will release our bus request by raising MemBusReq. If we wanted to initiate a burst read, we would continue to hold MemBustReq_n low Once the Memory Model reads the data, it will output it on bus MemData_InReg.

    The waveform is shown below:


    Writing data to the Memory Model is similar to the read operation explained above. You should be able to follow the waveform below and deduce its operation.



    Alignment logic

    The final block is this part of your design is the alignment logic. This block has the function of reconciling the 32-bit word organization of the memory withthe byte addresses coming out of the CPU. The first step in this process is to realize that each memory word contains four byte and thus the address input to the memory model will always be the CPU address shifted right by two bits. The low order two bits will select the particular byte lane within the 32-bit word that is returned.


    The second part of the problem has to do with the fact when the CPU asks for a byte or a word access, it always expects that the byte will be in the low order 8-bits of the input bus. However, unless the low order two bits of the address are zero, this byte will not be in the low order byte lane of the memory word. The function of the data alignment network is to examine the low order two bits of the CPU address and in the case of LB instructions, shift the selected byte lane of the incoming memory word to the low order byte.


    An analogous problem arises for half-word accesses. Since there will be two 16-bit halfword lanes in each memory word.


    Store byte and store half-word instructions present a special problem. If the CPU asks to write a particular byte lane or half-word lane of a 32-bit memory work, you must do so without overwriting the other parts of the memory word. Thus, Store byte and Store halfword instruction must pre-read the contents of the memory word in question, insert the byte or half-word and then write the memory word. Do this last, but you will need to design both your control unit and aligment network to handle this case.


    Building and Running a Test Program

    We mentioned above that it is possible to point the memory model to an initialization file through the generic declaraction attached to the symbol. We will talk more about this in a moment. First, we need to know how this file can be created. As an diagnostic program for your design we have written as supplied to a test program written in MIPs assembler called simpletest.s. To familarize yourself with how this program works, you may wish to download a copy and run it in the SPIM simulator. A full program listing with load addresses and machine language shown is available in simpletest.lst. We have also prepared a loadable memory image of this program, with the first instruction set up to load into memory location 0x000000. This file can be found in simpletest.bin.


    As mentioned in the class, there are two possible versions for the branch implementation. If you implemented as stated in the textbook Appendix A (NextPC = PC + Offset), then go ahead and use simpletest.bin. However, if you implemented as stated in the diagramm in the middle of the book and the branch description on the webpage (NextPC = PC + 4 + Offset), then you should use the patched version of the bin file, which you can get here:simpletest2.bin.


    1) Make a copy of this file and place it in your group directory.

    2) Modify the generic in your memory model component to point to this file.

    3) Compile and run your design in the simulator. You should have setup your reset line to force the PC to location zero. Start your simulation run by toggling the reset line, then monitor the memory data bus as the program executes. When finished you can monitor the data transfer of the registers back into memory to verify your results.