summaryrefslogtreecommitdiffstats
path: root/PIC/state_machine.vhd
diff options
context:
space:
mode:
Diffstat (limited to 'PIC/state_machine.vhd')
-rw-r--r--PIC/state_machine.vhd224
1 files changed, 224 insertions, 0 deletions
diff --git a/PIC/state_machine.vhd b/PIC/state_machine.vhd
new file mode 100644
index 0000000..464e0bc
--- /dev/null
+++ b/PIC/state_machine.vhd
@@ -0,0 +1,224 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+use work.alu_types.all;
+
+use work.decoder.all;
+
+entity state_machine is
+ port(
+ clk : in std_logic;
+ opcode : in std_logic_vector(13 downto 0);
+
+ reset : in std_logic;
+
+ op : out alu_op;
+ we_mem : out std_logic := '0';
+ re_mem : out std_logic := '0';
+ we_w : out std_logic := '0';
+ instr_ret : out std_logic := '0';
+ bit_select : out std_logic_vector(2 downto 0) := (others => '0');
+ data : out std_logic_vector(7 downto 0) := (others => '0');
+ pc : out std_logic_vector(12 downto 0);
+ addr : out std_logic_vector(6 downto 0) := (others => '0');
+
+ use_literal : out std_logic;
+ we_status : out std_logic := '0';
+
+ stack_push : out std_logic := '0';
+ stack_pop : out std_logic := '0';
+ stack_din : out std_logic_vector(12 downto 0) := (others => '0');
+ stack_dout : in std_logic_vector(12 downto 0);
+
+ alu_skip : in std_logic;
+ alu_result : in std_logic_vector(7 downto 0)
+ );
+
+
+end entity state_machine;
+
+architecture rtl of state_machine is
+ type state_t is (IFetch, MRead, Execute, MWrite);
+
+ signal pc_internal : std_logic_vector(12 downto 0) := (others => '0');
+ signal state : state_t := IFetch;
+ signal pclath : std_logic_vector(4 downto 0) := (others => '0');
+
+begin
+ pc <= pc_internal;
+
+ fsm : process(clk)
+ variable instr : instruction_t;
+
+ -- do we do a phantom nop after this instruction?
+ variable do_phantom_nop : boolean := false;
+ variable is_phantom : boolean := false;
+ -- true when phantom came from DECFSZ/INCFSZ skip (must still increment PC)
+ -- false when phantom came from GOTO/CALL/RETURN (PC already at target)
+ variable is_skip_phantom : boolean := false;
+ begin
+
+ if rising_edge(clk) then
+
+ re_mem <= '0';
+ we_mem <= '0';
+ we_w <= '0';
+ we_status <= '0';
+ instr_ret <= '0';
+ stack_push <= '0';
+ stack_pop <= '0';
+
+ if reset = '1' then
+ pc_internal <= (others => '0');
+ pclath <= (others => '0');
+ state <= IFetch;
+ do_phantom_nop := false;
+ else
+
+ case state is
+ when IFetch =>
+
+ -- if we had a branch before, this cycle is a NOP.
+ if do_phantom_nop then
+ instr := instruction_decode((others => '0'));
+ do_phantom_nop := false;
+ is_phantom := true;
+ -- is_skip_phantom carries over from whoever set do_phantom_nop
+ else
+ instr := instruction_decode(opcode);
+ is_phantom := false;
+ is_skip_phantom := false;
+ end if;
+
+ -- set use_literal based on decoded instruction, holds through MWrite
+ if instr.class = LITERAL_OP then
+ use_literal <= '1';
+ else
+ use_literal <= '0';
+ end if;
+
+ -- for literal operations
+ data <= instr.k(7 downto 0);
+
+ -- for memory-read/write operations
+ addr <= instr.f;
+
+ -- for bit-select operations
+ bit_select <= instr.b;
+
+ state <= Mread;
+
+ when MRead =>
+
+ -- read memory addressed by the opcode
+ -- MOVWF, CLRF, CLRW, NOP are write-only.
+ if instr.class = BIT_OP or
+ (instr.class = BYTE_OP and
+ instr.op /= MOVWF and instr.op /= CLRF and
+ instr.op /= CLRW and instr.op /= NOP) then
+ re_mem <= '1';
+ end if;
+
+
+
+ state <= Execute;
+
+ when Execute =>
+
+ op <= instr.op; -- move the decoded instruction to the ALU
+
+ if not is_phantom then
+
+ -- increment PC or change address, different for different instructions.
+ if instr.op = GOTO then
+
+ pc_internal <= "00" & instr.k;
+ do_phantom_nop := true;
+ is_skip_phantom := false;
+
+ elsif instr.op = CALL then
+
+ -- push PC+1 to the stack
+ stack_din <= std_logic_vector(unsigned(pc_internal) + 1);
+ stack_push <= '1';
+
+ -- go to the function we called
+ pc_internal <= "00" & instr.k;
+ do_phantom_nop := true;
+ is_skip_phantom := false;
+
+ elsif instr.op = RETUR or instr.op = RETLW then
+
+ -- pop the PC from stack.
+ pc_internal <= stack_dout;
+ stack_pop <= '1'; -- pop deferred to next cycle.
+ do_phantom_nop := true;
+ is_skip_phantom := false;
+
+ else
+ pc_internal <= std_logic_vector(unsigned(pc_internal) + 1);
+ end if;
+
+ elsif is_skip_phantom then
+ -- skip phantom: PC was at the skipped instruction, step past it
+ pc_internal <= std_logic_vector(unsigned(pc_internal) + 1);
+ end if;
+ -- branch phantom: PC already at target, no change needed
+
+ state <= MWrite;
+
+ when MWrite =>
+
+ -- write to correct memory based on class (skip for phantom NOP)
+ if not is_phantom then
+ case instr.class is
+ when BYTE_OP =>
+ if instr.op /= NOP then
+ if instr.d = '1' then
+ we_mem <= '1';
+ -- Write to PCLATH (addr 0x0A): latch upper PC bits
+ if instr.f = "0001010" then
+ pclath <= alu_result(4 downto 0);
+ end if;
+ -- Write to PCL (addr 0x02): redirect PC, like a GOTO
+ if instr.f = "0000010" then
+ pc_internal <= pclath & alu_result;
+ do_phantom_nop := true;
+ is_skip_phantom := false;
+ end if;
+ else
+ we_w <= '1';
+ end if;
+ end if;
+ when BIT_OP =>
+ -- NOT for the BTFSS or BTFSC instructions.
+ if instr.op = BCF or instr.op = BSF then
+ we_mem <= '1';
+ end if;
+ when LITERAL_OP =>
+ if instr.op /= NOP and instr.op /= RETUR then
+ we_w <= '1';
+ end if;
+ when others => null;
+ end case;
+ end if;
+
+ -- DECFSZ/INCFSZ/BTFSS/BTFSC: skip next instruction if ALU signals skip
+ if not is_phantom and
+ (instr.op = DECFSZ or instr.op = INCFSZ or instr.op = BTFSS or instr.op = BTFSC) and alu_skip = '1' then
+ do_phantom_nop := true;
+ is_skip_phantom := true;
+ end if;
+
+ if not is_phantom then
+ we_status <= '1';
+ instr_ret <= '1';
+ end if;
+ state <= IFetch;
+
+ end case;
+ end if; -- reset
+ end if;
+ end process fsm;
+
+end architecture rtl;