-- Model Name : Reduced Activity - Parwan Control Unit -- Author : Zainalabedin Navabi -- 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_parameters.ALL;
USE WORK.synthesis_utilities.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;
      halted : OUT std_logic := '0'; ready : IN std_logic
      );
END par_control_unit;
--
ARCHITECTURE dataflow_synthesizable 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, ready )
      CONSTANT dis : TIME := 1 NS;
    BEGIN
      -- memory control and other external signals:
      read_mem <= '0';
      write_mem <= '0';
      halted <= '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;
          IF ready = '1' THEN
            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;
            PAR_CTRL.VHD 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;
          ELSE
            next_state <= instr_fetch;
          END IF;
        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 (7 DOWNTO 4) /= single_byte_instructions) THEN
            next_state <= opnd_fetch;
          ELSE
            CASE ir_out (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 =>
                halted <= '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;
          IF ready = '1' THEN
            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 (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 (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 (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;
          ELSE
            next_state <= opnd_fetch;
          END IF;
        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;
          IF ready = '1' THEN
            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;
          ELSE
            next_state <= do_indirect;
          END IF;
        WHEN do_two_bytes => --------------------------------------6
          IF ( ir_out (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 (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;
            IF ready = '1' THEN
              next_state <= initial;
            ELSE
              next_state <= do_two_bytes;
            END IF;
          ELSIF ( ir_out (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;
            IF ready = '1' THEN
              databus_on_dbus <= NOT databus_on_dbus'DRIVING_VALUE AFTER 1.6 ns; --1*dis;
              IF ( ir_out (6) = '0' ) THEN ---- lda, and
                IF ( ir_out (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 (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 <= do_two_bytes;
            END IF;
          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;
          IF ready = '1' THEN
            -- address of subroutine to pc
            load_offset_pc <= NOT load_offset_pc'DRIVING_VALUE AFTER 6*dis;--check it
            next_state <= continue_jsr;
          ELSE
            next_state <= do_jsr;
          END IF;
        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 AND ir_out (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 dataflow_synthesizable;