diff options
Diffstat (limited to 'PIC/state_machine.vhd')
| -rw-r--r-- | PIC/state_machine.vhd | 224 |
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; |
