Ben Eater Breadboard 8 bit computer on an FPGA

After spending weeks building the Ben Eater breadboard 8-bit computer, I decided that it would be a nice project to replicate on an FPGA and since the Ben Eater computer was completely built with logic devices, it can easily be built on an FPGA and VHDL was my language of choice because that is the Hardware Description Language I was familiar with. 

I started with creating all the registers like the accumulator, program counter, data and address registers. I also instantiated a 16 x 8 bit single port RAM which i used as the RAM of the entire system. I also implemented the control logic with finite state machines. The full quartus project can be found here. The code is shown below: 

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;

library altera_mf;
use altera_mf.altera_mf_components.all;

entity sap_1 is
port(
        clk, reset : in std_logic;
–register outputs
        pc_out : out std_logic_vector(3 downto 0);
        ac_out : out std_logic_vector(7 downto 0);
        dr_out : out std_logic_vector(7 downto 0);
        ar_out : out std_logic_vector(3 downto 0);
        cf_out, zf_out : out std_logic;
        mem_wr_out : out std_logic
);
end sap_1;

architecture arch of sap_1 is
        type state_type is(reset_pc,fetch, decode,execute_nop, execute_add, execute_sub,
        execute_sta, execute_sta2, execute_ldi, execute_jmp, execute_jc,
        execute_jz, execute_out, execute_hlt, execute_lda);
        signal state_reg, state_next : state_type;
        signal inst_reg, inst_next : std_logic_vector(7 downto 0);
        signal mem_data_reg : std_logic_vector(7 downto 0);
        signal mem_data : std_logic_vector(7 downto 0);
        signal ac_reg, ac_next : std_logic_vector(8 downto 0);
        signal pc_reg, pc_next : std_logic_vector(3 downto 0);
        signal mem_addr_reg : std_logic_vector(3 downto 0);
        signal mem_addr : std_logic_vector(3 downto 0);
        signal memory_write : std_logic;
        signal zf_reg, zf_next, cf_reg, cf_next : std_logic;
begin
        memory : altsyncram
        generic map(operation_mode => “SINGLE_PORT”,
              width_a => 8,
              widthad_a => 4,
              lpm_type => “altsyncram”,
              outdata_reg_a => “UNREGISTERED”,
              init_file => “program.mif”,
              intended_device_family => “Cyclone”)
        port map(wren_a => memory_write, clock0 => clk,
              address_a => mem_addr, data_a => ac_reg(7 downto 0),
              q_a => mem_data);
        process(clk, reset)
        begin
              if(reset = ‘1’) then
                      state_reg <= reset_pc;
                      inst_reg <= (others => ‘0’);
                      –mem_data_reg <= (others => ‘0’);
                      ac_reg <= (others => ‘0’);
                      pc_reg <= (others => ‘0’);
                      mem_addr_reg <= (others => ‘0’);
                     cf_reg <= ‘0’;
                     zf_reg <= ‘0’;
        elsif(clk’event and clk = ‘1’) then
                    state_reg <= state_next;
                    inst_reg <= inst_next;
                    mem_data_reg <= mem_data;
                   ac_reg <= ac_next;
                   pc_reg <= pc_next;
                   mem_addr_reg <= mem_addr;
                   cf_reg <= cf_next;
                   zf_reg <= zf_next;
        end if;
        end process;

        process(state_reg, inst_reg, mem_data_reg, mem_data, ac_reg, pc_reg, cf_reg, zf_reg)
        begin
              state_next <= state_reg;
              inst_next <= inst_reg;
              ac_next <= ac_reg;
              pc_next <= pc_reg;
              memory_write <= ‘0’;
              case state_reg is
                    when reset_pc =>
                          pc_next <= (others => ‘0’);
                          ac_next <= (others => ‘0’);
                          state_next <= fetch;
                    when fetch =>
                          inst_next <= mem_data;
                          pc_next <= pc_reg + 1;
                          state_next <= decode;
                    when decode =>
                          case inst_reg(7 downto 4) is
                                when “0000” =>
                                      state_next <= execute_nop;
                                when “0001” =>
                                      state_next <= execute_lda;
                                when “0010” =>
                                      state_next <= execute_add;
                                when “0011” =>
                                      state_next <= execute_sub;
                                when “0100” =>
                                      state_next <= execute_sta;
                                when “0101” =>
                                      state_next <= execute_ldi;
                                when “0110” =>
                                      state_next <= execute_jmp;
                                when “0111” =>
                                      state_next <= execute_jc;
                                when “1000” =>
                                      state_next <= execute_jz;
                                when “1110” =>
                                      state_next <= execute_out;
                                when “1111” =>
                                      state_next <= execute_hlt;
                                when others =>
                                      state_next <= fetch;
                          end case;
                    when execute_nop =>
                          state_next <= fetch;
                    when execute_lda =>
                          ac_next <= ‘0’ & mem_data;
                          state_next <= fetch;
                    when execute_add =>
                          ac_next <= (‘0’ & ac_reg(7 downto 0)) + (‘0’ & mem_data);
                          state_next <= fetch;
                    when execute_sub =>
                          ac_next <= (‘0’ & ac_reg(7 downto 0)) – (‘0’ & mem_data);
                          state_next <= fetch;
                    when execute_sta =>
                          memory_write <= ‘1’;
                          state_next <= execute_sta2;
                    when execute_sta2 =>
                          state_next <= fetch;
                    when execute_ldi =>
                          ac_next <= “00000” & inst_reg(3 downto 0);
                          state_next <= fetch;
                    when execute_jmp =>
                          pc_next <= inst_reg(3 downto 0);
                          state_next <= fetch;
                    when execute_jc =>
                          if(cf_reg = ‘1’) then
                                pc_next <= inst_reg(3 downto 0);
                          end if;
                          state_next <= fetch;
                    when execute_jz =>
                          if(zf_reg = ‘1’) then
                                pc_next <= inst_reg(3 downto 0);
                          end if;
                          state_next <= fetch;
                    when execute_out =>
                          state_next <= fetch;
                    when execute_hlt =>
                          –state_next <= fetch;
              end case;
        end process;

        mem_addr <= (others => ‘0’) when state_reg = reset_pc else
              pc_reg when state_reg = fetch else
              inst_reg(3 downto 0) when state_reg = decode else
              pc_reg when state_reg = execute_nop else
              pc_reg when state_reg = execute_add else
              pc_reg when state_reg = execute_sub else
              inst_reg(3 downto 0) when state_reg = execute_sta else
              pc_reg when state_reg = execute_sta2 else
              pc_reg when state_reg = execute_lda else
              pc_reg when state_reg = execute_ldi else
              inst_reg(3 downto 0) when state_reg = execute_jmp else
              inst_reg(3 downto 0) when (state_reg = execute_jz) and zf_reg = ‘1’ else
              pc_reg when (state_reg = execute_jz) and zf_reg = ‘0’ else
              inst_reg(3 downto 0) when (state_reg = execute_jc) and cf_reg = ‘1’ else
              pc_reg when (state_reg = execute_jc) and cf_reg = ‘0’ else
              pc_reg when state_reg = execute_out else
              pc_reg when state_reg = execute_hlt;

        zf_next <= ‘1’ when ac_reg = 0 else
              ‘0’;
        cf_next <= ‘1’ when ac_reg(8) = ‘1’ else
              ‘0’;
        pc_out <= pc_reg;
        ac_out <= ac_reg(7 downto 0);
        dr_out <= mem_data;
        ar_out <= mem_addr;
        cf_out <= cf_reg;
        zf_out <= zf_reg;
end arch;