OpenCores
URL https://opencores.org/ocsvn/neo430/neo430/trunk

Subversion Repositories neo430

[/] [neo430/] [trunk/] [neo430/] [rtl/] [core/] [neo430_control.vhd] - Rev 198

Compare with Previous | Blame | View Log

-- #################################################################################################
-- #  << NEO430 - Main Control Unit >>                                                             #
-- # ********************************************************************************************* #
-- # Central CPU control unit (micro sequencer / FSM).                                             #
-- # ********************************************************************************************* #
-- # BSD 3-Clause License                                                                          #
-- #                                                                                               #
-- # Copyright (c) 2020, Stephan Nolting. All rights reserved.                                     #
-- #                                                                                               #
-- # Redistribution and use in source and binary forms, with or without modification, are          #
-- # permitted provided that the following conditions are met:                                     #
-- #                                                                                               #
-- # 1. Redistributions of source code must retain the above copyright notice, this list of        #
-- #    conditions and the following disclaimer.                                                   #
-- #                                                                                               #
-- # 2. Redistributions in binary form must reproduce the above copyright notice, this list of     #
-- #    conditions and the following disclaimer in the documentation and/or other materials        #
-- #    provided with the distribution.                                                            #
-- #                                                                                               #
-- # 3. Neither the name of the copyright holder nor the names of its contributors may be used to  #
-- #    endorse or promote products derived from this software without specific prior written      #
-- #    permission.                                                                                #
-- #                                                                                               #
-- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS   #
-- # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF               #
-- # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE    #
-- # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,     #
-- # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE #
-- # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED    #
-- # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING     #
-- # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED  #
-- # OF THE POSSIBILITY OF SUCH DAMAGE.                                                            #
-- # ********************************************************************************************* #
-- # The NEO430 Processor - https://github.com/stnolting/neo430                                    #
-- #################################################################################################
 
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
 
library neo430;
use neo430.neo430_package.all;
 
entity neo430_control is
  port (
    -- global control --
    clk_i     : in  std_ulogic; -- global clock, rising edge
    rst_i     : in  std_ulogic; -- global reset, low-active, async
    -- memory interface --
    instr_i   : in  std_ulogic_vector(15 downto 0); -- instruction word from memory
    -- control --
    sreg_i    : in  std_ulogic_vector(15 downto 0); -- current status register
    ctrl_o    : out std_ulogic_vector(ctrl_width_c-1 downto 0); -- control signals
    irq_vec_o : out std_ulogic_vector(01 downto 0); -- irq channel address
    imm_o     : out std_ulogic_vector(15 downto 0); -- branch offset
    -- irq lines --
    irq_i     : in  std_ulogic_vector(03 downto 0)  -- IRQ lines^
  );
end neo430_control;
 
architecture neo430_control_rtl of neo430_control is
 
  -- instruction register --
  signal ir      : std_ulogic_vector(15 downto 0);
  signal ir_wren : std_ulogic;
 
  -- branch system --
  signal branch_taken : std_ulogic;
 
  -- state machine --
  type state_t is (RESET, IFETCH_0, IFETCH_1, DECODE,
    TRANS_0, TRANS_1, TRANS_2, TRANS_3, TRANS_4, TRANS_5, TRANS_6,
    PUSHCALL_0, PUSHCALL_1, PUSHCALL_2,
    RETI_0, RETI_1, RETI_2, RETI_3,
    IRQ_0, IRQ_1, IRQ_2, IRQ_3, IRQ_4, IRQ_5);
  signal state, state_nxt  : state_t;
  signal ctrl_nxt, ctrl    : std_ulogic_vector(ctrl_width_c-1 downto 0);
  signal am_nxt, am        : std_ulogic_vector(03 downto 0); -- addressing mode
  signal mem_rd, mem_rd_ff : std_ulogic; -- memory read buffers
  signal src_nxt, src      : std_ulogic_vector(03 downto 0); -- source reg
  signal sam_nxt, sam      : std_ulogic_vector(01 downto 0); -- CMD according SRC addressing mode
 
  -- irq system --
  signal irq_fire               : std_ulogic;
  signal irq_start, irq_ack     : std_ulogic;
  signal irq_ack_mask, irq_buf  : std_ulogic_vector(3 downto 0);
  signal irq_vec_nxt, irq_vec   : std_ulogic_vector(1 downto 0);
  signal i_flag_ff0, i_flag_ff1 : std_ulogic;
 
begin
 
  -- Branch Condition Check ---------------------------------------------------
  -- -----------------------------------------------------------------------------
  cond_check: process(instr_i, sreg_i)
  begin
    case instr_i(12 downto 10) is -- condition
      when cond_ne_c => branch_taken <= not sreg_i(sreg_z_c); -- JNE/JNZ
      when cond_eq_c => branch_taken <= sreg_i(sreg_z_c); -- JEQ/JZ
      when cond_lo_c => branch_taken <= not sreg_i(sreg_c_c); -- JNC/JLO
      when cond_hs_c => branch_taken <= sreg_i(sreg_c_c); -- JC/JHS
      when cond_mi_c => branch_taken <= sreg_i(sreg_n_c); -- JN
      when cond_ge_c => branch_taken <= not (sreg_i(sreg_n_c) xor sreg_i(sreg_v_c)); -- JGE
      when cond_le_c => branch_taken <= sreg_i(sreg_n_c) xor sreg_i(sreg_v_c); -- JL
      when cond_al_c => branch_taken <= '1'; -- JMP (always)
      when others    => branch_taken <= '0'; -- undefined
    end case;
  end process cond_check;
 
  -- branch offset (sign-extended) from instruction REGISTER --
  imm_o <= ir(9) & ir(9) & ir(9) & ir(9) & ir(9) & ir(9 downto 0) & '0';
 
 
  -- Arbiter State Machine Sync -----------------------------------------------
  -- -----------------------------------------------------------------------------
  arbiter_sync0: process(rst_i, clk_i)
  begin
    -- the arbiter requires a defined initial state
    if (rst_i = '0') then
      state <= RESET; -- this is crucial!
    elsif rising_edge(clk_i) then
      state <= state_nxt;
    end if;
  end process arbiter_sync0;
 
  arbiter_sync1: process(clk_i)
  begin
    if rising_edge(clk_i) then
      -- these signals do not need a specific reset state
      ctrl      <= ctrl_nxt;
      src       <= src_nxt;
      mem_rd_ff <= mem_rd;
      am        <= am_nxt;
      sam       <= sam_nxt;
      if (ir_wren = '1') then
        ir <= instr_i; -- instruction register
      end if;
    end if;
  end process arbiter_sync1;
 
  -- control bus output --
  ctrl_o <= ctrl;
 
  -- someone using the DADD instruction? --
  dadd_sanity_check: process(clk_i)
  begin
    if rising_edge(clk_i) then
      if (instr_i(15 downto 12) = "1010") then -- DADD
        assert false report "DADD instruction not supported!" severity error;
      end if;
    end if;
  end process dadd_sanity_check;
 
 
  -- Arbiter State Machine Comb -----------------------------------------------
  -- -----------------------------------------------------------------------------
  arbiter_comb: process(state, instr_i, ir, ctrl, branch_taken, src, am, sam, mem_rd_ff, irq_start, sreg_i)
    variable spec_cmd_v, valid_wb_v, move_cmd_v : std_ulogic;
  begin
 
    -- NOTES --
    -- Signals in states/sub states marked with a "-->" are moved out of case statement and are set as pseudo default.
    -- The general assigning of this signal does not effect states, which actually do not require this signal.
    -- However, this saves some mux logic in the states.
 
    -- arbiter defaults --
    state_nxt <= state;
    src_nxt   <= src; -- source reg
    am_nxt    <= am;  -- total addressing mode [OP class I/II, src_addr_mode(1), src_addr_mode(0), dst_addr_mode]
    sam_nxt   <= sam; -- default source addressing mode
    ir_wren   <= '0'; -- write to instruction register
    mem_rd    <= '0'; -- normal ("slow") memory read
    irq_ack   <= '0'; -- ack irq to irq-controller
 
    -- control defaults --
    ctrl_nxt <= (others => '0'); -- all off
    ctrl_nxt(ctrl_rf_adr3_c  downto ctrl_rf_adr0_c)  <= src; -- source reg A
    ctrl_nxt(ctrl_alu_cmd3_c downto ctrl_alu_cmd0_c) <= ctrl(ctrl_alu_cmd3_c downto ctrl_alu_cmd0_c); -- keep ALU function
    ctrl_nxt(ctrl_rf_as1_c downto ctrl_rf_as0_c) <= sam; -- default SRC addressing mode
    ctrl_nxt(ctrl_adr_off2_c downto ctrl_adr_off0_c) <= "010"; -- add +2 as address offset
    ctrl_nxt(ctrl_mem_rd_c) <= mem_rd_ff; -- delayed memory read
    ctrl_nxt(ctrl_alu_bw_c) <= ctrl(ctrl_alu_bw_c); -- keep byte/word mode
 
    -- special single ALU operation? --
    spec_cmd_v := '0';
    if (ir(15 downto 9) = "0001001") then -- CALL or PUSH or RETI
      spec_cmd_v := '1';
    end if;
 
    -- is MOV operation? --
    move_cmd_v := '0';
    -- use ctrl's signals here, since MOV operation can be set by IR and by the FSM itself
    if (ctrl(ctrl_alu_cmd3_c downto ctrl_alu_cmd0_c) = alu_mov_c) then
      move_cmd_v := '1';
    end if;
 
    -- valid write back? --
    valid_wb_v := '1';
    if (ir(15 downto 12) = alu_cmp_c) or (ir(15 downto 12) = alu_bit_c) then
      valid_wb_v := '0'; -- CMP and BIT instructions only write status flags
    end if;
 
    -- state machine --
    case state is
 
      when RESET => -- init PC with boot address
      -- ------------------------------------------------------------
        ctrl_nxt(ctrl_rf_boot_c) <= '1'; -- load boot address
        ctrl_nxt(ctrl_rf_wb_en_c) <= '1'; -- valid RF write back
        ctrl_nxt(ctrl_rf_adr3_c downto ctrl_rf_adr0_c) <= reg_pc_c; -- source/destination: PC
        state_nxt <= IFETCH_0;
 
 
      when IFETCH_0 => -- output and update PC & IRQ check (stay here for SLEEP)
      -- ------------------------------------------------------------
        sam_nxt <= "00"; -- SRC address mode = REG, required for all special operations + IRQ
        ctrl_nxt(ctrl_alu_bw_c) <= '0'; -- word mode, also required for all IRQ states
        ctrl_nxt(ctrl_rf_adr3_c downto ctrl_rf_adr0_c) <= reg_pc_c; -- source/destination: PC
        ctrl_nxt(ctrl_adr_off2_c downto ctrl_adr_off0_c) <= "010"; -- add +2
        ctrl_nxt(ctrl_rf_in_sel_c) <= '1'; -- select addr gen feedback
        ctrl_nxt(ctrl_adr_bp_en_c) <= '1'; -- directly output PC/IRQ vector
        if (irq_start = '1') then -- execute IRQ
          state_nxt <= IRQ_0;
        elsif (sreg_i(sreg_s_c) = '0') then -- no sleep mode = normal execution
          ctrl_nxt(ctrl_mem_rd_c) <= '1'; -- Memory read (fast)
          ctrl_nxt(ctrl_rf_wb_en_c) <= '1'; -- valid RF write back
          state_nxt <= IFETCH_1;
        end if;
 
      when IFETCH_1 => -- wait for memory
      -- ------------------------------------------------------------
        state_nxt <= DECODE;
 
 
      when DECODE => -- decode applied instruction & store it to IR
      -- ------------------------------------------------------------
        ir_wren <= '1'; -- update instruction register
        ctrl_nxt(ctrl_alu_bw_c) <= instr_i(6); -- byte/word mode
        sam_nxt <= instr_i(5 downto 4);
        ctrl_nxt(ctrl_rf_adr3_c downto ctrl_rf_adr0_c) <= reg_pc_c; -- source/destination: PC (used by branch instructions only)
        ctrl_nxt(ctrl_adr_off2_c downto ctrl_adr_off0_c) <= "000"; -- add immediate offset (used by branch instructions only)
        ctrl_nxt(ctrl_rf_in_sel_c) <= '1'; -- select addr gen feedback (used by branch instructions only)
 
        if (instr_i(15 downto 14) = "00") then -- branch or format II instruction
          if (instr_i(13) = '1') then -- BRANCH INSTRUCTION
          -- ------------------------------------------------------------
            ctrl_nxt(ctrl_rf_wb_en_c) <= branch_taken; -- valid RF write back if branch taken
            state_nxt <= IFETCH_0;
 
          elsif (instr_i(12 downto 10) = "100") then -- FORMAT II INSTRUCTION
            -- ------------------------------------------------------------
            am_nxt(0) <= instr_i(4) or instr_i(5); -- dst addressing mode
            am_nxt(3) <= '0'; -- class II
            if (instr_i(3 downto 0) = reg_cg_c) or ((instr_i(3 downto 0) = reg_sr_c) and (instr_i(5) = '1')) then -- source special?
              am_nxt(2 downto 1) <= "00"; -- source addressing mode
            else
              am_nxt(2 downto 1) <= instr_i(5 downto 4); -- source addressing mode
            end if;
            src_nxt <= instr_i(3 downto 0); -- src is also dst
            if (instr_i(15 downto 9) /= "0001001") then -- not PUSH/CALL/RETI?
              ctrl_nxt(ctrl_alu_cmd3_c downto ctrl_alu_cmd0_c) <= "00" & instr_i(8 downto 7); -- ALU function (rrc/swpb/rra/sxt)
            else
              ctrl_nxt(ctrl_alu_cmd3_c downto ctrl_alu_cmd0_c) <= alu_mov_c; -- to move OpA -> RF/MEM
            end if;
            case instr_i(9 downto 7) is
              when "100"  => state_nxt <= TRANS_0;  -- PUSH (via single ALU OP)
              when "101"  => state_nxt <= TRANS_0;  -- CALL (via single ALU OP)
              when "110"  => state_nxt <= RETI_0;   -- RETI
              when "111"  => state_nxt <= IFETCH_0; -- !!!UNDEFINED OPCODE!!!
              when others => state_nxt <= TRANS_0;  -- single ALU OP (FORMAT II)
            end case;
 
          else -- !!!UNDEFINED OPCODE!!!
            -- ------------------------------------------------------------
            state_nxt <= IFETCH_0;
          end if;
 
        else -- FORMAT I INSTRUCTION
        -- ------------------------------------------------------------
          am_nxt(3) <= '1'; -- class I
          if (instr_i(11 downto 8) = reg_cg_c) or ((instr_i(11 downto 8) = reg_sr_c) and (instr_i(5) = '1')) then -- source special?
            am_nxt(2 downto 1) <= "00"; -- source addressing mode for r2 & r3
          else
            am_nxt(2 downto 1) <= instr_i(5 downto 4); -- source addressing mode
          end if;
          am_nxt(0) <= instr_i(7); -- dst addressing mode
          ctrl_nxt(ctrl_alu_cmd3_c downto ctrl_alu_cmd0_c) <= instr_i(15 downto 12); -- ALU function
          src_nxt <= instr_i(11 downto 8);
          if (instr_i(15 downto 12) = "1010") then -- !!!INVALID ALUOP!!!
            state_nxt <= IFETCH_0;
          else
            state_nxt <= TRANS_0;
          end if;
        end if;
 
 
      when TRANS_0 => -- operand transfer cycle 0
      -- ------------------------------------------------------------
        -- (pseudo) defaults 
        ctrl_nxt(ctrl_rf_in_sel_c) <= '1'; -- select addr gen feedback (only relevant for when 2,3,5)
        ctrl_nxt(ctrl_mem_rd_c) <= '1'; -- Memory read (fast)
        ctrl_nxt(ctrl_adr_bp_en_c) <= '1'; -- directly output RF.out to address bus
        ctrl_nxt(ctrl_adr_mar_wr_c) <= am(0); -- write to MAR [relevant for memory writeback when using CLASS II operations]
        --
        case am is -- addressing mode
          when "0001" | "0000" | "1000" =>
            -- "0001" = CLASS II, SRC: Reg, DST: Indexed
            -- "0000" = CLASS II, SRC/DST: register direct
            -- "1000" = CLASS  I, SRC/DST: Reg
            ctrl_nxt(ctrl_rf_adr3_c downto ctrl_rf_adr0_c) <= src; -- source: reg A
            ctrl_nxt(ctrl_alu_opa_wr_c) <= '1'; -- write OpA
            if (spec_cmd_v = '1') then -- push or call
              state_nxt <= PUSHCALL_0;
            elsif (am(3) = '0') then -- CLASS II operation
              state_nxt <= TRANS_6;
            else
              state_nxt <= TRANS_3;
            end if;
 
          when "1001"   |   "1010" | "0010"  | "0011" =>
            -- "1001" = CLASS  I, SRC: register direct, DST: indexed
            --
            -- "1010" = CLASS  I, SRC: indexed/symbolic/absolute, DST: register direct
            -- "001-" = CLASS II, SRC: indexed/symbolic/absolute, DST: indexed/symbolic/absolute OR register direct
            ctrl_nxt(ctrl_rf_adr3_c downto ctrl_rf_adr0_c) <= reg_pc_c; -- source/destination: PC
-->         ctrl_nxt(ctrl_adr_bp_en_c) <= '1'; -- directly output RF.out to address bus
-->         ctrl_nxt(ctrl_mem_rd_c) <= '1'; -- Memory read (fast)
            ctrl_nxt(ctrl_adr_off2_c downto ctrl_adr_off0_c) <= "010"; -- add +2
-->         ctrl_nxt(ctrl_rf_in_sel_c) <= '1'; -- select addr gen feedback
            ctrl_nxt(ctrl_rf_wb_en_c) <= '1'; -- valid RF write back
            if (am(2 downto 1) = "00") then -- "1001" = CLASS  I, SRC: register direct, DST: indexed
              state_nxt <= TRANS_3;
            else
              state_nxt <= TRANS_2;
            end if;
 
          when "1011" =>
            -- "1011" = CLASS  I, SRC: indexed/symbolic/absolute, DST: indexed
            ctrl_nxt(ctrl_rf_adr3_c downto ctrl_rf_adr0_c) <= reg_pc_c; -- source/destination: PC
            ctrl_nxt(ctrl_adr_mar_wr_c) <= '1'; -- write to MAR
            mem_rd <= '1'; -- Memory read
-->         ctrl_nxt(ctrl_mem_rd_c) <= '1'; -- Memory read (fast)
            ctrl_nxt(ctrl_adr_off2_c downto ctrl_adr_off0_c) <= "010"; -- add +2
-->         ctrl_nxt(ctrl_rf_in_sel_c) <= '1'; -- select addr gen feedback
            ctrl_nxt(ctrl_rf_wb_en_c) <= '1'; -- valid RF write back
            state_nxt <= TRANS_1;
 
          when "0100" | "0101" | "1100" | "1101" =>
            -- "010-" = CLASS II, SRC/DST: indirect
            -- "1100" = CLASS  I, SRC: indirect, DST: register direct
            -- "1101" = CLASS  I, SRC: indirect, DST: indexed
            ctrl_nxt(ctrl_rf_adr3_c downto ctrl_rf_adr0_c) <= src; -- source: reg A
-->         cctrl_nxt(ctrl_adr_bp_en_c) <= '1'; -- directly output RF.out to address bus
-->         ctrl_nxt(ctrl_mem_rd_c) <= '1'; -- Memory read (fast)
            state_nxt <= TRANS_1;
 
          when others =>
            -- "011-" = CLASS II, SRC/DST: indirect auto inc
            -- "1110" = CLASS  I, SRC: indirect auto inc, DST: register direct
            -- "1111" = CLASS  I, SRC: indirect auto inc, DST: indexed
            ctrl_nxt(ctrl_rf_adr3_c downto ctrl_rf_adr0_c) <= src; -- source: reg A
-->         cctrl_nxt(ctrl_adr_bp_en_c) <= '1'; -- directly output RF.out to address bus
-->         ctrl_nxt(ctrl_mem_rd_c) <= '1'; -- Memory read (fast)
            if (ir(6) = '0') or (src = reg_pc_c) then -- word mode (force if accessing PC)
              ctrl_nxt(ctrl_adr_off2_c downto ctrl_adr_off0_c) <= "010"; -- add +2
            else -- byte mode
              ctrl_nxt(ctrl_adr_off2_c downto ctrl_adr_off0_c) <= "001"; -- add +1
            end if;
-->         ctrl_nxt(ctrl_rf_in_sel_c) <= '1'; -- select addr gen feedback
            ctrl_nxt(ctrl_rf_wb_en_c) <= '1'; -- valid RF write back
            state_nxt <= TRANS_1;
        end case;
 
      when TRANS_1 => -- operand transfer cycle 1
      -- ------------------------------------------------------------
        -- (pseudo) defaults 
        ctrl_nxt(ctrl_rf_in_sel_c) <= '1'; -- select addr gen feedback (only relevant for last two 'when')
        ctrl_nxt(ctrl_mem_rd_c) <= '1'; -- Memory read (fast) (only relevant for last two 'when' 4)
        mem_rd <= '1'; -- Memory read (only relevant for last two 'when' 5)
        ctrl_nxt(ctrl_adr_off2_c downto ctrl_adr_off0_c) <= "010"; -- add +2
        ctrl_nxt(ctrl_rf_adr3_c downto ctrl_rf_adr0_c) <= reg_pc_c; -- source/destination: PC
        --
        case am is -- addressing mode
          -- "000-" = CLASS II, SRC/DST: register direct                   [ACTUAL DON'T CARE; STATE NOT USED]
          -- "1000" = CLASS  I, SRC: register direct, DST: register direct [ACTUAL DON'T CARE; STATE NOT USED]
          -- "1001" = CLASS  I, SRC: register direct, DST: indexed         [ACTUAL DON'T CARE; STATE NOT USED]
          -- ==> DONT CARE
 
          -- "001-" = CLASS II, SRC/DST: indexed/symbolic/absolute                   [ACTUAL DON'T CARE; STATE NOT USED]
          -- "1010" = CLASS  I, SRC: indexed/symbolic/absolute, DST: register direct [ACTUAL DON'T CARE; STATE NOT USED]
          -- ==> DONT CARE
 
          when "0100" | "0101" | "1100"   |   "0110" | "0111" | "1110" =>
            -- "010-" = CLASS II, SRC/DST: indirect
            -- "1100" = CLASS  I, SRC: indirect, DST: register direct
            --
            -- "011-" = CLASS II, SRC/DST: indirect auto inc,
            -- "1110" = CLASS  I, SRC: indirect auto inc, DST: register direct
            ctrl_nxt(ctrl_alu_in_sel_c) <= '1'; -- get data from memory
            ctrl_nxt(ctrl_alu_opa_wr_c) <= '1'; -- write OpA
            if (spec_cmd_v = '1') then -- push or call
              state_nxt <= PUSHCALL_0;
            else
              if (am(3) = '0') then -- CLASS II
                state_nxt <= TRANS_6;
              else -- CLASS I
                state_nxt <= TRANS_3;
              end if;
            end if;
 
          when "1101"  |  "1111" =>
            -- "1101" = CLASS  I, SRC: indirect, DST: indexed
            --
            -- "1111" = CLASS  I, SRC: indirect auto inc, DST: indexed
-->         ctrl_nxt(ctrl_rf_adr3_c downto ctrl_rf_adr0_c) <= reg_pc_c; -- source/destination: PC
            ctrl_nxt(ctrl_adr_bp_en_c) <= '1'; -- directly output RF.out to address bus
-->         ctrl_nxt(ctrl_mem_rd_c) <= '1'; -- Memory read (fast)
-->         ctrl_nxt(ctrl_adr_off2_c downto ctrl_adr_off0_c) <= "010"; -- add +2
-->         ctrl_nxt(ctrl_rf_in_sel_c) <= '1'; -- select addr gen feedback 
            ctrl_nxt(ctrl_rf_wb_en_c) <= '1'; -- valid RF write back
            ctrl_nxt(ctrl_alu_in_sel_c) <= '1'; -- get data from memory
            ctrl_nxt(ctrl_alu_opa_wr_c) <= '1'; -- write OpA
            state_nxt <= TRANS_3; -- no PUSHCALL check required
 
          when others =>
            -- "1011" = CLASS  I, SRC: indexed/symbolic/absolute, DST: indexed
-->         ctrl_nxt(ctrl_rf_adr3_c downto ctrl_rf_adr0_c) <= reg_pc_c; -- source/destination: PC
            ctrl_nxt(ctrl_adr_mar_wr_c) <= '1'; -- write to MAR
-->         mem_rd <= '1'; -- Memory read
-->         ctrl_nxt(ctrl_adr_off2_c downto ctrl_adr_off0_c) <= "010"; -- add +2
-->         ctrl_nxt(ctrl_rf_in_sel_c) <= '1'; -- select addr gen feedback 
            ctrl_nxt(ctrl_rf_wb_en_c) <= '1'; -- valid RF write back
            state_nxt <= TRANS_2;
        end case;
 
      when TRANS_2 => -- operand transfer cycle 2
      -- ------------------------------------------------------------
        -- "001-" = CLASS II: SRC/DST: indexed/symbolic/absolute
        -- "1010" = CLASS  I, SRC:     indexed/symbolic/absolute, DST: register direct
        -- "1011" = CLASS  I, SRC:     indexed/symbolic/absolute, DST: indexed
        -- OTHERS = DONT CARE [state not reachable]
        ctrl_nxt(ctrl_rf_adr3_c downto ctrl_rf_adr0_c) <= src; -- source: reg A
        ctrl_nxt(ctrl_adr_off2_c downto ctrl_adr_off0_c) <= "1--"; -- add memory data in (only relevant for first 'when')
        ctrl_nxt(ctrl_adr_mar_sel_c) <= '1'; -- use result from adder
        ctrl_nxt(ctrl_adr_off2_c downto ctrl_adr_off0_c) <= "1--"; -- add memory data in
        ctrl_nxt(ctrl_adr_mar_wr_c) <= '1'; -- write to MAR
        mem_rd <= '1'; -- Memory read
        mem_rd <= '1'; -- Memory read
        state_nxt <= TRANS_3;
 
      when TRANS_3 => -- operand transfer cycle 3
      -- ------------------------------------------------------------
        -- (pseudo) defaults 
        ctrl_nxt(ctrl_rf_adr3_c downto ctrl_rf_adr0_c) <= ir(3 downto 0); -- source: reg B
        ctrl_nxt(ctrl_adr_mar_sel_c) <= '1'; -- use result from adder
        ctrl_nxt(ctrl_adr_off2_c downto ctrl_adr_off0_c) <= "1--"; -- add memory data in
        --
        case am is -- addressing mode
          when "1001" | "1011" | "1101" | "1111" =>
            -- "1001" = CLASS  I, SRC: register direct, DST: indexed
            -- "1011" = CLASS  I, SRC: indexed/symbolic/absolute, DST: indexed
            -- "1101" = CLASS  I, SRC: indirect, DST: indexed
            -- "1111" = CLASS  I, SRC: indirect auto inc, DST: indexed
            ctrl_nxt(ctrl_rf_as1_c) <= '0'; -- DST address mode = REG or INDEXED
            ctrl_nxt(ctrl_rf_as0_c) <= ir(7); -- DST address mode = REG or INDEXED
-->         ctrl_nxt(ctrl_adr_off2_c downto ctrl_adr_off0_c) <= "1--"; -- add memory data in
-->         ctrl_nxt(ctrl_adr_mar_sel_c) <= '1'; -- use result from adder
            ctrl_nxt(ctrl_adr_mar_wr_c) <= '1'; -- write to MAR
            mem_rd <= '1'; -- Memory read
            state_nxt <= TRANS_4;
 
          when others =>
            ctrl_nxt(ctrl_rf_as1_c downto ctrl_rf_as0_c) <= "00"; -- DST address mode = REG
            ctrl_nxt(ctrl_alu_opb_wr_c) <= '1'; -- write OpB
            if (am(2 downto 1) = "01") then
              state_nxt <= TRANS_4;
            else
              state_nxt <= TRANS_6;
            end if;
        end case;
 
      when TRANS_4 => -- operand transfer cycle 4
      -- ------------------------------------------------------------
        -- (pseudo) defaults 
        ctrl_nxt(ctrl_rf_adr3_c downto ctrl_rf_adr0_c) <= src; -- source: reg A (only relevant for 3rd 'when')
        --
        case am is -- addressing mode
          when "0010" | "0011" | "1010" | "1011" =>
            -- "001-" = CLASS II, SRC/DST: indexed/symbolic/absolute
            -- "1010" = CLASS  I, SRC: indexed/symbolic/absolute, DST: register direct
            -- "1011" = CLASS  I, SRC: indexed/symbolic/absolute, DST: indexed
            ctrl_nxt(ctrl_alu_in_sel_c) <= '1'; -- get data from memory
            ctrl_nxt(ctrl_alu_opa_wr_c) <= '1'; -- write to OpA
            if (spec_cmd_v = '1') then -- push or call
              state_nxt <= PUSHCALL_0;
            elsif ((am(3) and am(0)) = '1') and (move_cmd_v = '0') then -- skip processing of second operand when executing MOV instruction
              state_nxt <= TRANS_5;
            else
              state_nxt <= TRANS_6;
            end if;
 
          when "1001" =>
            -- "1001" = CLASS  I, SRC: register direct, DST: indexed
            ctrl_nxt(ctrl_alu_opa_wr_c) <= '1'; -- write OpA
            if (move_cmd_v = '1') then -- skip processing of second operand when executing MOV instruction
              state_nxt <= TRANS_6;
            else
              state_nxt <= TRANS_5;
            end if;
 
          when others => -- NOP / DONT CARE
            -- "000-" = CLASS II, SRD/DST:  DONT CARE
            -- "1000" = CLASS  I, SRC: register direct, DST: register direct = DONT CARE
            -- "-10-" : NOP
            -- "-11-" : NOP
            if (move_cmd_v = '1') then -- skip processing of second operand when executing MOV instruction
              state_nxt <= TRANS_6;
            else
              state_nxt <= TRANS_5;
            end if;
        end case;
 
      when TRANS_5 => -- operand transfer cycle 5
      -- ------------------------------------------------------------
        ctrl_nxt(ctrl_alu_in_sel_c) <= '1'; -- get data from memory
        ctrl_nxt(ctrl_alu_opb_wr_c) <= '1'; -- write to OpB
        state_nxt <= TRANS_6;
 
      when TRANS_6 => -- operand transfer cycle 6: RF or MEM write-back
      -- ------------------------------------------------------------
        ctrl_nxt(ctrl_rf_adr3_c downto ctrl_rf_adr0_c) <= ir(3 downto 0); -- destination
        ctrl_nxt(ctrl_rf_fup_c) <= not spec_cmd_v; -- update ALU status flags
        if (am(0) = '0') then -- DST: register direct (register file)
          ctrl_nxt(ctrl_rf_wb_en_c) <= valid_wb_v; -- valid RF write back (not for CMP/BIT!)
        else -- DST: indexed (memory)
          ctrl_nxt(ctrl_mem_wr_c) <= valid_wb_v; -- valid MEM write back (not for CMP/BIT!)
        end if;
        state_nxt <= IFETCH_0; -- done!
 
 
      when PUSHCALL_0 => -- PUSH/CALL cycle 0 (stack update)
      -- ------------------------------------------------------------
        ctrl_nxt(ctrl_alu_cmd3_c downto ctrl_alu_cmd0_c) <= alu_mov_c; -- keep this for all following states
        ctrl_nxt(ctrl_rf_adr3_c downto ctrl_rf_adr0_c) <= reg_sp_c; -- source/destination: SP
        ctrl_nxt(ctrl_adr_off2_c downto ctrl_adr_off0_c) <= "011"; -- add -2
        ctrl_nxt(ctrl_adr_mar_wr_c) <= '1'; -- write to MAR
        ctrl_nxt(ctrl_adr_mar_sel_c) <= '1'; -- use result from adder
        ctrl_nxt(ctrl_rf_in_sel_c) <= '1'; -- select addr gen feedback
        ctrl_nxt(ctrl_rf_wb_en_c) <= '1'; -- valid RF write back
        if (ir(7) = '1') then -- CALL
          state_nxt <= PUSHCALL_1;
        else -- PUSH
          state_nxt <= PUSHCALL_2;
        end if;
 
      when PUSHCALL_1 => -- CALL cycle 1 (buffer PC so it can be written to memory)
      -- ------------------------------------------------------------
        ctrl_nxt(ctrl_rf_adr3_c downto ctrl_rf_adr0_c) <= reg_pc_c; -- source/destination: PC
        ctrl_nxt(ctrl_alu_opa_wr_c) <= '1'; -- write to OpA
        ctrl_nxt(ctrl_rf_wb_en_c) <= '1'; -- valid RF write back
        state_nxt <= PUSHCALL_2;
 
      when PUSHCALL_2 => -- PUSH/CALL cycle 2 (write data to memory)
      -- ------------------------------------------------------------
        ctrl_nxt(ctrl_mem_wr_c) <= '1'; -- memory write request
        state_nxt <= IFETCH_0; -- done!
 
 
      when RETI_0 => -- RETI cycle 0: Output address of old SR; SP=SP+2
      -- ------------------------------------------------------------
        ctrl_nxt(ctrl_alu_cmd3_c downto ctrl_alu_cmd0_c) <= alu_mov_c; -- keep this for all following states
        ctrl_nxt(ctrl_rf_adr3_c downto ctrl_rf_adr0_c) <= reg_sp_c; -- source/destination: SP
        ctrl_nxt(ctrl_adr_bp_en_c) <= '1'; -- directly output RF.out to address bus
        ctrl_nxt(ctrl_mem_rd_c) <= '1'; -- Memory read (fast)
        ctrl_nxt(ctrl_adr_off2_c downto ctrl_adr_off0_c) <= "010"; -- add +2
        ctrl_nxt(ctrl_rf_in_sel_c) <= '1'; -- select addr gen feedback
        ctrl_nxt(ctrl_rf_wb_en_c) <= '1'; -- valid RF write back
        state_nxt <= RETI_1;
 
      when RETI_1 => -- RETI cycle 1: Buffer status register from MEM in OpA; Output address of old PC; SP=SP+2
      -- ------------------------------------------------------------
        ctrl_nxt(ctrl_rf_adr3_c downto ctrl_rf_adr0_c) <= reg_sp_c; -- source/destination: SP
        ctrl_nxt(ctrl_adr_bp_en_c) <= '1'; -- directly output RF.out to address bus
        ctrl_nxt(ctrl_mem_rd_c) <= '1'; -- Memory read (fast)
        ctrl_nxt(ctrl_adr_off2_c downto ctrl_adr_off0_c) <= "010"; -- add +2
        ctrl_nxt(ctrl_rf_in_sel_c) <= '1'; -- select addr gen feedback
        ctrl_nxt(ctrl_rf_wb_en_c) <= '1'; -- valid RF write back
        ctrl_nxt(ctrl_alu_in_sel_c) <= '1'; -- get data from memory
        ctrl_nxt(ctrl_alu_opa_wr_c) <= '1'; -- write to OpA
        state_nxt <= RETI_2;
 
      when RETI_2 => -- RETI cycle 4: Write status register; buffer return address from MEM in OpA
      -- ------------------------------------------------------------
        ctrl_nxt(ctrl_alu_in_sel_c) <= '1'; -- get data from memory
        ctrl_nxt(ctrl_alu_opa_wr_c) <= '1'; -- write to OpA
        ctrl_nxt(ctrl_rf_adr3_c downto ctrl_rf_adr0_c) <= reg_sr_c; -- destination: SR
        ctrl_nxt(ctrl_rf_wb_en_c) <= '1'; -- valid RF write back
        state_nxt <= RETI_3;
 
      when RETI_3 => -- RETI cycle 5: Write return address to PC
      -- ------------------------------------------------------------
        ctrl_nxt(ctrl_rf_adr3_c downto ctrl_rf_adr0_c) <= reg_pc_c; -- destination: PC
        ctrl_nxt(ctrl_rf_wb_en_c) <= '1'; -- valid RF write back
        state_nxt <= IFETCH_0; -- done!
 
 
      when IRQ_0 => -- IRQ processing cycle 0: SP=SP-2, disable sleep mode
      -- ------------------------------------------------------------
        ctrl_nxt(ctrl_alu_cmd3_c downto ctrl_alu_cmd0_c) <= alu_mov_c; -- keep this for all following states
        ctrl_nxt(ctrl_rf_adr3_c downto ctrl_rf_adr0_c) <= reg_sp_c; -- source/destination: SP
        ctrl_nxt(ctrl_adr_mar_wr_c)  <= '1'; -- write to MAR
        ctrl_nxt(ctrl_adr_off2_c downto ctrl_adr_off0_c) <= "011"; -- add -2
        ctrl_nxt(ctrl_adr_mar_sel_c) <= '1'; -- use result from adder
        ctrl_nxt(ctrl_rf_in_sel_c)   <= '1'; -- select addr gen feedback
        ctrl_nxt(ctrl_rf_wb_en_c)    <= '1'; -- valid RF write back
        ctrl_nxt(ctrl_rf_dsleep_c)   <= '1'; -- disable sleep mode
        state_nxt <= IRQ_1;
 
      when IRQ_1 => -- IRQ processing cycle 1: Buffer PC for memory write
      -- ------------------------------------------------------------
        ctrl_nxt(ctrl_rf_adr3_c downto ctrl_rf_adr0_c) <= reg_pc_c; -- source: PC
        ctrl_nxt(ctrl_alu_opa_wr_c) <= '1'; -- write PC to OpA
        state_nxt <= IRQ_2;
 
      when IRQ_2 => -- IRQ processing cycle 2: Write PC to memory (push), SP=SP-2
      -- ------------------------------------------------------------
        ctrl_nxt(ctrl_mem_wr_c)      <= '1'; -- write memory request (store PC)
        ctrl_nxt(ctrl_rf_adr3_c downto ctrl_rf_adr0_c) <= reg_sp_c; -- source/destination: SP
        ctrl_nxt(ctrl_adr_mar_wr_c)  <= '1'; -- write to MAR
        ctrl_nxt(ctrl_adr_off2_c downto ctrl_adr_off0_c) <= "011"; -- add -2
        ctrl_nxt(ctrl_adr_mar_sel_c) <= '1'; -- use result from adder
        ctrl_nxt(ctrl_rf_in_sel_c)   <= '1'; -- select addr gen feedback
        ctrl_nxt(ctrl_rf_wb_en_c)    <= '1'; -- valid RF write back
        state_nxt <= IRQ_3;
 
      when IRQ_3 => -- IRQ processing cycle 3: Buffer SR for memory write, set IRQ vector address, disable interrupt enable flag in SR
      -- ------------------------------------------------------------
        ctrl_nxt(ctrl_rf_adr3_c downto ctrl_rf_adr0_c) <= reg_sr_c; -- source: SR
        ctrl_nxt(ctrl_alu_opa_wr_c)  <= '1'; -- write SR to OpA
        ctrl_nxt(ctrl_adr_bp_en_c)   <= '1'; -- directly output PC/IRQ vector
        ctrl_nxt(ctrl_adr_ivec_oe_c) <= '1'; -- output IRQ vector
        ctrl_nxt(ctrl_mem_rd_c)      <= '1'; -- Memory read (fast)
        ctrl_nxt(ctrl_rf_dgie_c)     <= '1'; -- disable interrupt enable flag
        irq_ack                      <= '1'; -- acknowledge started IRQ handler
        state_nxt <= IRQ_4;
 
      when IRQ_4 => -- IRQ processing cycle 4: Write SR (push), get IRQ vector
      -- ------------------------------------------------------------
        ctrl_nxt(ctrl_mem_wr_c)     <= '1'; -- write memory request
        ctrl_nxt(ctrl_alu_in_sel_c) <= '1'; -- get data from memory
        ctrl_nxt(ctrl_alu_opa_wr_c) <= '1'; -- write to OpA
        state_nxt <= IRQ_5;
 
      when IRQ_5 => -- IRQ processing cycle 5: Store IRQ vector to PC
      -- ------------------------------------------------------------
        ctrl_nxt(ctrl_rf_adr3_c downto ctrl_rf_adr0_c) <= reg_pc_c; -- destination: PC
        ctrl_nxt(ctrl_rf_wb_en_c) <= '1'; -- valid RF write back
        state_nxt <= IFETCH_0; -- done!
 
 
      when others => -- invalid
      -- ------------------------------------------------------------
        state_nxt <= RESET;
 
    end case;
  end process arbiter_comb;
 
 
  -- Interrupt Controller -----------------------------------------------------
  -- -----------------------------------------------------------------------------
  irq_buffer: process(clk_i)
  begin
    if rising_edge(clk_i) then
      -- delay I flag 2 cycles to allow the interrupted program to execute at least one insruction even if we have
      -- a permanent interrupt request
      i_flag_ff0 <= sreg_i(sreg_i_c);
      i_flag_ff1 <= i_flag_ff0;
      -- interrupt vector and queue buffer --
      for i in 0 to 3 loop
        irq_buf(i) <= (irq_buf(i) or irq_i(i)) and (not sreg_i(sreg_q_c)) and (not irq_ack_mask(i));
      end loop; -- i
      -- interrupt control --
      if (irq_start = '0') or (sreg_i(sreg_i_c) = '0') then -- idle or IRQs disabled
        irq_start <= '0';
        if (irq_fire = '1') then -- IRQ triggered
          irq_vec   <= irq_vec_nxt; -- capture source
          irq_start <= '1';
        end if;
      else -- active IRQ
        if (irq_ack = '1') then -- handler started?
          irq_start <= '0';
        end if;
      end if;
    end if;
  end process irq_buffer;
 
  -- valid start of IRQ handler --
  irq_fire <= '1' when (irq_buf /= "0000") and (i_flag_ff1 = '1') and (sreg_i(sreg_i_c) = '1') else '0';
 
  -- acknowledge mask --
  irq_ack_mask_gen: process(irq_ack, irq_vec)
    variable irq_tmp_v : std_ulogic_vector(2 downto 0);
  begin
    irq_tmp_v := irq_ack & irq_vec;
    case irq_tmp_v is
      when "100"  => irq_ack_mask <= "0001";
      when "101"  => irq_ack_mask <= "0010";
      when "110"  => irq_ack_mask <= "0100";
      when "111"  => irq_ack_mask <= "1000";
      when others => irq_ack_mask <= "0000";
    end case;
  end process;
 
  -- interrupt priority encoder --
  irq_priority: process(irq_buf)
  begin
    -- use "case" here to avoid a MUX chain
    case irq_buf is
      when "0001" | "0011" | "0101" | "0111" | "1001" | "1011" | "1101" | "1111" => -- "---1"
        irq_vec_nxt <= "00";
      when "0010" | "0110" | "1010" | "1110" => -- "--10"
        irq_vec_nxt <= "01";
      when "0100" | "1100" => -- "-100"
        irq_vec_nxt <= "10";
      when others => -- "1000" ("0000" -> dont't care)
        irq_vec_nxt <= "11";
    end case;
  end process irq_priority;
 
  -- interrupt vector output --
  irq_vec_o <= irq_vec; -- the final address is constructed in the address generator
 
 
end neo430_control_rtl;
 

Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.