These notes are to help understand the inside mechanics of a writable computer part, a stack. The stack acts like a deep dual input register (DIR). You write to it and read from it like a register with the difference that values can be pushed into it and then popped out of it in the order that they went in. Hence the nick-name "push-down stack". The block diagram illustrates what the architecture could be, but it is not how it might be when the VHDL code for the part is compiled.
The purple lines are the control lines. The blue lines are the data flow lines. The orange lines are internal control flow for the index into the register array. The DIR, index, register array and flip flop are all clocked on the positive edge.
The push and pop control whether the index into the register array increments, decrements or stays the same. The push signal is captured in the flip flop to write to the register array and select the proper source for the data output. The push signal must be delayed to synchronize it with the data in the DIR and the address in the index register which are also clocked. The delayed push signal selects the output data to be from the DIR and writes the DIR to the register array. The push signal is used to write to the DIR.
If the number of registers in the register array is set to zero, then all the support blocks are not needed, the stack reduces to a DIR and the pop signal is not connected. In the VHDL code, this optimization is done when the generic value for the stack depth is set to zero.
An alternative stateless, implementation for the stack:
In this diagram, the push signal is not delayed since the data and index are not delayed. This implementation has one less flip flop for the push signal but the four to one mux has to pass all the bits in the index register. Comparing it to the previous implementation, only 0, 1 or -1 had to pass through the mux where theoretically only two bits should be required to pass through the mux. The appeal of this implementation is that it is stateless, the data is written to the register array immediately. To allow for one of two locations to be written to (store goes to the current location; push goes to the next location) two addresses must be calculated and the proper one chosen through the mux. Because of this, the register array can't feed the data_out signal directly because it may change locations part way through a cycle when a push or pop signal is applied, so the register array output must be captured in a latch. To allow for reading and writing to the register array, the data must be captured on the same clock edge that it is written to the register array so this requires a mux before the output latch. While this circuit has one less flip flop than the previous one, there are two arithmetic circuits instead of one. The first circuit should be less hardware.
With synchronous RAM, a lot of the design can be absorbed into it.
-------------------------------------------------------------------------- -- Stack for stack processor Rob Chapman Mar 2, 1998 -- Values are pushed onto the stack and popped off when needed. If push -- and pop are asserted, then a store is performed. Library IEEE; use IEEE.STD_Logic_1164.all; use ieee.std_logic_arith.all; use ieee.std_logic_unsigned.all; entity Stack is generic ( width : positive := 8; widthad : positive := 3; depth : positive := 8 ); port ( clock, push, pop, d1_d2 : in std_logic; din1, din2 : in std_logic_vector( width-1 downto 0 ); reset : in std_logic; -- addressout : out std_logic_vector( widthad-1 downto 0 ); dout : out std_logic_vector( width-1 downto 0 )); end; architecture behaviour of Stack is subtype stackcell is std_logic_vector(width-1 downto 0); type Stack is array ( depth-1 downto 0 ) of stackcell; -- basic stack -- internal signals signal din : stackcell; -- data input to RAM signal index : std_logic_vector(widthad-1 downto 0); signal address : std_logic_vector(widthad-1 downto 0); signal pushpop : std_logic_vector(1 downto 0); -- for push&pop constant addresszero : std_logic_vector(widthad - 1 downto 0) := (others => '0'); signal next_out : stackcell; begin -- OUTPUT -- stack_ram : process(address, din, push, clock ) is variable stack_file : Stack; begin if rising_edge(clock) then if push = '1' then stack_file(conv_integer(index)) := din; next_out <= din; else next_out <= stack_file(conv_integer(index)); end if; end if; end process; send_on_falling : process(clock) is begin if falling_edge(clock) then dout <= next_out; end if; end process; -- select the input data -- process(d1_d2, din1, din2) begin if d1_d2 = '0' then din <= din1; else din <= din2; end if; end process; -- assign push and pop as a control vector pushpop(1) <= push; pushpop(0) <= pop; -- RAM address calculation next_address : process(clock, reset) is begin if reset = '1' then address <= addresszero; else case pushpop is when "10" => -- push address <= index - 1; -- subtract one to change the index when "01" => -- pop address <= index + 1; -- add one to change the index when others => -- do nothing address <= index; end case; end if; end process; -- stack address becomes index set_index : process(clock, reset) is begin if reset = '1' then index <= addresszero; else if falling_edge(clock) then -- wait until clock = '1'; index <= address; end if; end if; end process; end;
This information has been assembled by Rob Chapman for EE552.