Addressing a memory

We need to put our instructions in memory.

Design a memory where the program can be stored. A memory implementation in VHDL is shown in Figure 11.

library ieee;
use ieee.std_logic_1164.all; 
use ieee.numeric_std.all;
use ieee.std_logic_textio.all;

library std;
use std.textio.all;

library work;
use work.string_lib.all;

entity memory is
  generic (address_width: integer := 32;
           data_width: integer := 32;
           size: integer := 256);
  port(
    clk: in std_logic;
    write_enable: in std_logic; 
    address: in std_logic_vector(address_width-1 downto 0); 
    data_in: in std_logic_vector(data_width-1 downto 0);
    data_out: out std_logic_vector(data_width-1 downto 0));

end memory;

architecture rtl of memory is

  type memory_type is array(0 to size-1) of
    std_logic_vector(data_width-1  downto 0); 

  impure function init_memory return memory_type is
    file in_file: text is in "memory_contents.txt";
    variable in_line: line;
    variable s: std_logic_vector(data_width-1 downto 0);
    variable memory: memory_type; 
  begin
    report "length of memory is " & integer'image(memory'length); 
    for i in 1 to integer(memory'length) loop
      if not endfile(in_file) then
        readline(in_file, in_line);
        hread(in_line, s);
        memory(i-1) := s; 
      else
        memory(i-1) := (others => 'X');
      end if; 
    end loop;
    return memory;
  end function;
  
  signal memory: memory_type := init_memory;

begin

  update: process(clk)
  begin
    if rising_edge(clk) then 
      if write_enable = '1' then
        memory(to_integer(unsigned(address))) <= data_in;
      end if;
    end if;
  end process;
  
  data_out <= memory(to_integer(unsigned(address))); 

end rtl; 

Figure 11. A memory in VHDL.

This the VHDL view - other views are Verilog - SystemC-TLM

Create a pc that reads addresses expressed in bytes. Meaning that it increments itself with four for each instruction read. A program counter implementation in VHDL is shown in Figure 12.

library ieee;
use ieee.std_logic_1164.all; 
use ieee.numeric_std.all;

entity pc is
  generic (pc_width: integer := 32); 
  port(
    clk: in std_logic;
    pc_out: out std_logic_vector(pc_width-1 downto 0) := (others => '0'));
  end pc;

architecture rtl of pc is

  signal pc_value: std_logic_vector(pc_width-1 downto 0) := (others => '0');

begin

  update: process(clk)
  begin
    if rising_edge(clk) then
      pc_value <= std_logic_vector(unsigned(pc_value) + 4);
    end if;
  end process; 

  pc_out <= pc_value;

end rtl; 

Figure 12. A program counter in VHDL.

This the VHDL view - other views are Verilog - SystemC-TLM

Connect the pc and the memory into a design, so that when it runs, the program is read, and printed.

We define signals, such as pc

  signal pc_value: std_logic_vector(address_width-1 downto 0);

and data read from the memory

  signal data_out: std_logic_vector(data_width-1 downto 0);

and clock signal, as

  signal clk: std_logic := '0';

  constant clk_half_period: time := 2 ns; 
  constant n_clk_cycles: integer := 7; 

The clock signal is generated as

  clk_gen: process is
  begin
    for i in 1 to n_clk_cycles loop
      clk <= '0';
      wait for clk_half_period;
      clk <= '1';
      wait for clk_half_period; 
    end loop;
    wait; 
  end process;