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.
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.
- Setup and Creating a RAM
Create a new project in a new folder for lab 6 (e.g. stacker).
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.
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.
- 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) );
- Define RAM component
Copy the port statement from the RAM VHDL file put it in a component statement in your architecture header section.
- 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 (you need enough bits for eight states).
- 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.
- 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.
- 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".
- Simulate the circuit
Simulating this circuit is slightly more complex because we are using the built-in Altera functions. 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.vhd
ghdl -e --ieee=synopsys -fexplicit --work=altera_mf stackertest
ghdl -r stackertest --vcd=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.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.