-- Model Name : Concurrent - Controller -- Author : Armita Peymandoust -- Last Updated : 09 / 15 / 1996 -- This document is © copyrighted by the Author.

LIBRARY IEEE;
USE IEEE.std_logic_1164.ALL;
--
LIBRARY EXEMPLAR;
USE EXEMPLAR.exemplar_1164.ALL;
--
LIBRARY WORK;
USE WORK.synthesis_utilities.ALL;
USE WORK.synthesis_parameters.ALL;
USE WORK.alu_operations.ALL;
USE WORK.global_environment.ALL;
--
ENTITY par_control_unit IS
  PORT (
        -- register control signals:
        load_ac, zero_ac, 
        load_ir, 
        increment_pc, load_page_pc, load_offset_pc, reset_pc,
        load_page_mar, load_offset_mar, 
        load_sr, cm_carry_sr, 
        -- bus connection control signals:
        pc_on_mar_page_bus, ir_on_mar_page_bus, 
        pc_on_mar_offset_bus, dbus_on_mar_offset_bus,
        pc_offset_on_dbus, obus_on_dbus, databus_on_dbus, 
        mar_on_adbus,
        dbus_on_databus,
        -- logic unit function control outputs:
        arith_shift_left, arith_shift_right, no_shift,
        alu_operate : OUT std_logic := '0';
        alu_code : OUT std_logic_vector (2 DOWNTO 0) := ('0', '0', '0');
        -- memory control and other external signals:
        read_mem, write_mem : OUT std_logic; interrupt : IN std_logic
       );
END par_control_unit;
--
ARCHITECTURE Concurrent OF par_control_unit IS 
  TYPE cpu_states IS (initial, instr_fetch, do_one_bytes, opnd_fetch, 
                      do_indirect, do_two_bytes, do_jsr, continue_jsr, 
                      do_branch);
  SIGNAL present_state, next_state : cpu_states;
BEGIN
  clocking : PROCESS (cck, interrupt)
  BEGIN
    IF (interrupt = '1') THEN 
      present_state <= initial;
    ELSIF cck'EVENT THEN
      present_state <= next_state;
    END IF;
  END PROCESS clocking;  
  --
  sequencing : PROCESS ( present_state, interrupt )
    CONSTANT dis : TIME := 1 NS;
  BEGIN 
    -- memory control and other external signals:
    read_mem <= '0'; 
    write_mem <= '0';
    CASE present_state IS
      WHEN initial =>   -------------------------------------------1
        IF (interrupt = '1') THEN
          reset_pc <= NOT reset_pc'DRIVING_VALUE AFTER 1*dis;
          next_state <= initial;
        ELSE
          -- pc to mar
          pc_on_mar_page_bus <= NOT pc_on_mar_page_bus'DRIVING_VALUE AFTER 1*dis;
          pc_on_mar_offset_bus <= NOT pc_on_mar_offset_bus'DRIVING_VALUE AFTER 1*dis;
          load_page_mar <= NOT load_page_mar'DRIVING_VALUE AFTER 2*dis;
          load_offset_mar <= NOT load_offset_mar'DRIVING_VALUE AFTER 5*dis;
          next_state <= instr_fetch;
        END IF;
      WHEN instr_fetch =>   ---------------------------------------2
        -- read memory into ir
        mar_on_adbus <= NOT mar_on_adbus'DRIVING_VALUE AFTER 1*dis;
        read_mem <= '1' AFTER 1*dis;
        databus_on_dbus <= NOT databus_on_dbus'DRIVING_VALUE AFTER 1.6 ns;--1*dis;
        alu_operate <= NOT alu_operate'DRIVING_VALUE AFTER 2*dis;
        alu_code <= a_input AFTER 2*dis;
        no_shift <= NOT no_shift'DRIVING_VALUE AFTER 3*dis; 
        load_ir <= NOT load_ir'DRIVING_VALUE AFTER 4*dis;
        -- increment pc
        increment_pc <= NOT increment_pc'DRIVING_VALUE AFTER 1*dis;
        next_state <= do_one_bytes;
      WHEN do_one_bytes =>   --------------------------------------3
        pc_on_mar_page_bus <= NOT pc_on_mar_page_bus'DRIVING_VALUE AFTER 1*dis;
        pc_on_mar_offset_bus <= NOT pc_on_mar_offset_bus'DRIVING_VALUE AFTER 4*dis;
        load_page_mar <= NOT load_page_mar'DRIVING_VALUE AFTER 2*dis;
        load_offset_mar <= NOT load_offset_mar'DRIVING_VALUE AFTER 5*dis;
        IF (ir_out.val (7 DOWNTO 4) /= single_byte_instructions) THEN
          next_state <= opnd_fetch;
        ELSE
          CASE ir_out.val (3 DOWNTO 0) IS
            WHEN cla => 
              zero_ac <= NOT zero_ac'DRIVING_VALUE AFTER 4*dis;
              load_ac <= NOT load_ac'DRIVING_VALUE AFTER 4*dis;
            WHEN cma => 
              alu_operate <= NOT alu_operate'DRIVING_VALUE AFTER 2*dis;
              alu_code <= b_compl AFTER 2*dis;
              no_shift <= NOT no_shift'DRIVING_VALUE AFTER 3*dis; 
              load_sr <= NOT load_sr'DRIVING_VALUE AFTER 4*dis;
              load_ac <= NOT load_ac'DRIVING_VALUE AFTER 4*dis;
            WHEN cmc =>
              cm_carry_sr <= NOT cm_carry_sr'DRIVING_VALUE AFTER 4*dis;
            WHEN asl => 
              alu_operate <= NOT alu_operate'DRIVING_VALUE AFTER 2*dis;
              alu_code <= b_input AFTER 2*dis;
              arith_shift_left <= NOT arith_shift_left'DRIVING_VALUE AFTER 3*dis;
              load_sr <= NOT load_sr'DRIVING_VALUE AFTER 4*dis;
              load_ac <= NOT load_ac'DRIVING_VALUE AFTER 4*dis;
            WHEN asr => 
              alu_operate <= NOT alu_operate'DRIVING_VALUE AFTER 2*dis;
              alu_code <= b_input AFTER 2*dis;
              arith_shift_right <= NOT arith_shift_right'DRIVING_VALUE AFTER 3*dis;
              load_sr <= NOT load_sr'DRIVING_VALUE AFTER 4*dis;
              load_ac <= NOT load_ac'DRIVING_VALUE AFTER 4*dis;
            WHEN hlt => 
              halt <= '1';
            WHEN OTHERS => NULL;
          END CASE;
          next_state <= instr_fetch;
        END IF;
      WHEN opnd_fetch =>   ----------------------------------------4
        -- read memory into mar offset
        mar_on_adbus <= NOT mar_on_adbus'DRIVING_VALUE AFTER 1*dis;
        read_mem <= '1' AFTER 1*dis;
        databus_on_dbus <= NOT databus_on_dbus'DRIVING_VALUE AFTER 1.6 ns;--1*dis;
        dbus_on_mar_offset_bus <= NOT dbus_on_mar_offset_bus'DRIVING_VALUE AFTER 4*dis;
        load_offset_mar <= NOT load_offset_mar'DRIVING_VALUE AFTER 5*dis;
        IF ( ir_out.val (7 DOWNTO 6) /= jsr_or_bra ) THEN
          ir_on_mar_page_bus <= NOT ir_on_mar_page_bus'DRIVING_VALUE AFTER 1*dis;
          load_page_mar <= NOT load_page_mar'DRIVING_VALUE AFTER 2*dis;
          IF ( ir_out.val (4) = indirect ) THEN
            next_state <= do_indirect;
          ELSE
            next_state <= do_two_bytes;
          END IF;
        ELSE --jsr or bra, do not alter mar page
          IF ( ir_out.val (5) = '0' ) THEN  -- jsr
            next_state <= do_jsr;
          ELSE
            next_state <= do_branch;
          END IF;
        END IF;
        increment_pc <= NOT increment_pc'DRIVING_VALUE AFTER 1*dis;
      WHEN do_indirect =>   ---------------------------------------5
        -- read actual operand from memory into mar offset
        mar_on_adbus <= NOT mar_on_adbus'DRIVING_VALUE AFTER 1*dis;
        read_mem <= '1' AFTER 1*dis;
        databus_on_dbus <= NOT databus_on_dbus'DRIVING_VALUE AFTER 1.6 ns; --1*dis;
        dbus_on_mar_offset_bus <= NOT dbus_on_mar_offset_bus'DRIVING_VALUE AFTER 4*dis;
        load_offset_mar <= NOT load_offset_mar'DRIVING_VALUE AFTER 5*dis;
        next_state <= do_two_bytes;
      WHEN do_two_bytes =>   --------------------------------------6
        IF ( ir_out.val (7 DOWNTO 5) = jmp ) THEN 
          load_page_pc <= NOT load_page_pc'DRIVING_VALUE AFTER 1*dis;
          load_offset_pc <= NOT load_offset_pc'DRIVING_VALUE AFTER 1*dis;
          next_state <= instr_fetch;
        ELSIF ( ir_out.val (7 DOWNTO 5) = sta ) THEN
          -- mar on adbus, ac on databus, write to memory
          mar_on_adbus <= NOT mar_on_adbus'DRIVING_VALUE AFTER 1*dis;
          alu_operate <= NOT alu_operate'DRIVING_VALUE AFTER 2*dis;
          alu_code <=  b_input AFTER 2*dis;
          no_shift <= NOT no_shift'DRIVING_VALUE AFTER 3*dis; 
          obus_on_dbus <= NOT obus_on_dbus'DRIVING_VALUE AFTER 4*dis;
          dbus_on_databus <= NOT dbus_on_databus'DRIVING_VALUE AFTER 5*dis;
          write_mem <= '1' AFTER 6*dis;
          next_state <= initial;
        ELSIF ( ir_out.val (7) = '0' ) THEN               ------ lda, and, add, sub
          -- mar on adbus, read memory for operand, perform operation
          mar_on_adbus <= NOT mar_on_adbus'DRIVING_VALUE AFTER 1*dis;
          read_mem <= '1' AFTER 1*dis;
          databus_on_dbus <= NOT databus_on_dbus'DRIVING_VALUE AFTER 1.6 ns; --1*dis;
          IF ( ir_out.val (6) = '0' ) THEN                     ---- lda, and
            IF ( ir_out.val (5) = '0' ) THEN                     -- lda
              alu_operate <= NOT alu_operate'DRIVING_VALUE AFTER 2*dis;
              alu_code <= a_input AFTER 2*dis;
              no_shift <= NOT no_shift'DRIVING_VALUE AFTER 3*dis; 
            ELSE                                               -- and
              alu_operate <= NOT alu_operate'DRIVING_VALUE AFTER 2*dis;
              alu_code <= a_and_b AFTER 2*dis;
              no_shift <= NOT no_shift'DRIVING_VALUE AFTER 3*dis; 
            END IF;
          ELSE                                               ---- add, sub
            IF ( ir_out.val (5) = '0' ) THEN                     -- add
              alu_operate <= NOT alu_operate'DRIVING_VALUE AFTER 2*dis;
              alu_code <= a_add_b AFTER 2*dis;
              no_shift <= NOT no_shift'DRIVING_VALUE AFTER 3*dis; 
            ELSE                                               -- sub
              alu_operate <= NOT alu_operate'DRIVING_VALUE AFTER 2*dis;
              alu_code <= a_sub_b AFTER 2*dis;
              no_shift <= NOT no_shift'DRIVING_VALUE AFTER 3*dis; 
            END IF;
          END IF;
          load_sr <= NOT load_sr'DRIVING_VALUE AFTER 4*dis;
          load_ac <= NOT load_ac'DRIVING_VALUE AFTER 4*dis;
          next_state <= initial;
        ELSE
          next_state <= initial; --never happens
        END IF;
      WHEN do_jsr =>   --------------------------------------------7
        -- write pc offset to top of subroutine
        mar_on_adbus <= NOT mar_on_adbus'DRIVING_VALUE AFTER 1*dis;
        pc_offset_on_dbus <= NOT pc_offset_on_dbus'DRIVING_VALUE AFTER 1.6 ns; --1*dis;
        dbus_on_databus <= NOT dbus_on_databus'DRIVING_VALUE AFTER 5*dis;
        write_mem <= '1' AFTER 6*dis;
        -- address of subroutine to pc
        load_offset_pc <= NOT load_offset_pc'DRIVING_VALUE AFTER 6*dis;--check it
        next_state <= continue_jsr;
      WHEN continue_jsr =>   --------------------------------------8
        increment_pc <= NOT increment_pc'DRIVING_VALUE AFTER 1*dis;
        next_state <= initial;
      WHEN do_branch =>   -----------------------------------------9
        IF ( all_or (sr_out.val AND ir_out.val (3 DOWNTO 0)) = '1') THEN
          load_offset_pc <= NOT load_offset_pc'DRIVING_VALUE AFTER 1*dis;
        END IF;
        next_state <= initial;
    END CASE;
  END PROCESS sequencing;
END Concurrent;