CS 232: Lab 6

Title image Fall 2021

Lab 6: Memory and a Stack

The purpose of this lab is to give you experience working with random-access memory and a stack pointer. Reading and writing from memory is a multi-step process and requires setting signals in the proper order and giving the memory sufficient time to respond to the signals.

This labs focusses on the stack because it is an important component of any computer and will be used in next week's project, in which you build a CPU. The stack is just like the stack data structure with elements of fixed size. We use a registar (called a stack pointer) to keep track of the location of the next empty spot in the stack. When we push an element onto the stack, we write the element (taking data from a separate register) to the RAM (using the current stack pointer as the address), and increment the stack pointer. When we pop an element off of the stack, we decrement the stack pointer, then read the data at the address from the RAM.


Tasks

The overall task is to create a circuit consisting of a RAM (random access memory) and two registers. One register will be a stack pointer and one we'll call the memory buffer register [MBR]. The stack pointer will hold the address of the next free space in memory and will start with the value zero. Values moving to and from memory will be held in the MBR. The 7-segment display will always show the value of the MBR.

Four switches will specify a binary value from 0-15. Pressing button one should reset the circuit. Pressing button two will load the value from the switches into the MBR. Pressing button three will push the value in the MBR onto the stack. Pressing button four will pop the top value off the stack and place it in the MBR, unless the stack pointer's value is zero, in which case it does nothing. Pressing button one should reset the circuit, clearing the MBR and the stack pointer.

  1. Setup and Creating a RAM

    Create a new project in a new folder for lab 6 (e.g. stacker). In this project, you will need a VHDL file to implement the RAM.

    • If you are using Quartus,

      + (more detail)

      Using the Tools::MegaWizard Plug-in Manager, select 'Create a new custom megfunction variation', then select RAM from under 'Memory Compiler' and give it a name like memram_lab.

      Using the setup dialog, create a 1-port RAM that has 16 words of 4 bits each. Therefore, it should have 16 locations address bits and 4 data bits per location (if those values are not in the popup list, just type them in). Leave the default values for the rest of the selection panels. The output of the wizard will be a VHDL file from which we can copy the component and port map statements. When you are done, open the file and look at it. Note how the generic statements define the size and functionality of the memory circuit in the port map statement.

    • If you are not using Quartus

      + (more detail)

      Download memram_lab.vhd and save it in your lab 6 directory.
  2. Define the top-level entity

    Create a top-level VHDL file for your project (e.g. stacker). The entity should have the following port statement.

      port( reset: in std_logic; -- button 1
            clock: in std_logic;
            data:  in std_logic_vector(3 downto 0);
            b2:    in std_logic; -- switch values to mbr
            b3:    in std_logic; -- push mbr -> stack
            b4:    in std_logic; -- pop stack -> mbr
            value: out std_logic_vector(3 downto 0);
            stackview: out std_logic_vector(3 downto 0);
            stateview: out std_logic_vector(2 downto 0)
            );
  3. Define RAM component

    Copy the port statement from the RAM VHDL file put it in a component statement in your architecture header section.

  4. Define the internal signals and registers

    Define internal signals for the RAM_input (4-bit std_logic_vector), RAM_output (4-bit std_logic_vector), the RAM write enable signal RAM_we (std_logic), a stack_ptr (4-bit unsigned), a register called mbr (4-bit std_logic_vector), and the state variable (a std_logic_vector with enough bits for eight states).

  5. Port map the RAM

    Create a port map statement for the RAM circuit. Use the stack pointer for the RAM address, connect the clock, link the RAM_input to the data signal, link RAM_we to the wren signal, and link RAM_output to the q output signal.

  6. Connect the internal signals to their output signals

    Assign to the value output signal the MBR. Assign to the stackview output signal the stack_ptr, and assign to the stateview output the state. These should be concurrent signal assignments outside of any process.

  7. Define the main state machine

    Make a process statement that is sensitive to the clock and reset signals. This will be a standard state machine process. If the user hits reset (reset = '0'), then set the stack_ptr, mbr, RAM_input, RAM_we, and state signals all to zeros.

    In the rising edge condition, make a case statement. You will need six states. Each state should take the following action

    • State "000": this state is waiting for a button press. If button 2 is pressed, assign the data signal to the MBR and move to state "111". If button 3 is pressed, assign the MBR to the RAM_input, assign '1' to the RAM_we signal, and move to state "001". The RAM_we signal tells the RAM to write the value on the RAM_input to the address specified by the stack pointer. If button 4 is pressed, check if the stack pointer value is not zero. If it is not zero, then subtract one from the stack pointer, then move to state "100".
    • State "001": this is the next step in the process of writing to memory. Set the RAM_we back to '0', and increment the stack pointer so it has the address of the next free memory location. Move to state "111".
    • State "100": this is the next step in the read process. Since the address of the RAM was just modified in state "000", we need to wait for two clock cycles for the new address to be accepted and the proper output to appear. Do nothing except go to state "101".
    • State "101": Again, do nothing except go to state "110".
    • State "110": The output should be available, so assign the RAM_output to the MBR. Go to state "111".
    • State "111": This is a state that waits for all of the buttons to be released. If buttons 2, 3, and 4 all have the value '1', then go to state "000".
    • The when others case should go to state "000".

  8. Simulate the circuit

    Download stackertest.vhd as a testbench file.

    Simulating this circuit is slightly more complex because we are using the built-in Altera functions. Instructions are different, depending on whether you are using your own computer, or one of the dwarves or Apporto.

    • If you are on one of the dwarves or are using Apporto

      + (more detail)

      The following commands should enable you to simulate the circuit using ghdl and gtkwave. Note that you should execute the first instruction only the first time you run the simulation. Use stackertest.vhd to test your circuit.

      ghdl -a --ieee=synopsys -fexplicit --work=altera_mf /export/opt/altera/12.1/quartus/eda/sim_lib/altera*.vhd
      ghdl -a --ieee=synopsys -fexplicit --work=altera_mf stackertest.vhd stacker.vhd memram_lab.vhd
      ghdl -e --ieee=synopsys -fexplicit --work=altera_mf stackertest
      ghdl -r --ieee=synopsys -fexplicit --work=altera_mf stackertest --vcd=stackertest.vcd
      gtkwave stackertest.vcd&

      You may get many warnings in the -e stage. Only errors matter.

      If the above does not work, try the following.

      ghdl -c --ieee=synopsys -fexplicit --work=altera_mf /export/opt/altera/12.1/quartus/eda/sim_lib/altera*.vhd stackertest.vhd stacker.vhd memram_lab.vhd -r stackertest --vcd=stackertest.vcd

    • If you are using your own machine

      + (more detail)

      Download and unzip altera.zip.

      Compile and run the program in one fell swoop, replacing ~/Dropbox/cs232/altera with the path on your computer to the altera folder:

      ghdl -c --ieee=synopsys -fexplicit --work=altera_mf ~/Dropbox/cs232/altera/*.vhd stackertest.vhd stacker.vhd memram_lab.vhd -r stackertest --vcd=stackertest.vcd

      It is not necessary to close gtkwave each time you run the simulation. Instead, leave it running, execute the -a / -e / -r ghdl commands and then select File->Reload Waveform from within gtkwave or click the blue circular arrow.

      The output of your test should look like the following.

      The stackertest pushes 1, 2, 3 onto the stack then pops those values off the stack. In between pushing and popping it puts a 0 into the MBR so you can tell the first pop worked correctly.

    • Please hand in your stacker.vhd file and include your GHDL output in your lab report as the first task in the project.

When you have completed the lab assignment, go ahead and get started on the current project.