595 lines
No EOL
26 KiB
VHDL
595 lines
No EOL
26 KiB
VHDL
-- cpu.vhd: Simple 8-bit CPU (BrainFuck interpreter)
|
|
-- Copyright (C) 2024 Brno University of Technology,
|
|
-- Faculty of Information Technology
|
|
-- Author(s): Roman Necas <xnecasr00 AT stud.fit.vutbr.cz>
|
|
--
|
|
library ieee;
|
|
use ieee.std_logic_1164.all;
|
|
use ieee.std_logic_arith.all;
|
|
use ieee.std_logic_unsigned.all;
|
|
|
|
-- ----------------------------------------------------------------------------
|
|
-- Entity declaration
|
|
-- ----------------------------------------------------------------------------
|
|
entity cpu is
|
|
port (
|
|
CLK : in std_logic; -- hodinovy signal
|
|
RESET : in std_logic; -- asynchronni reset procesoru
|
|
EN : in std_logic; -- povoleni cinnosti procesoru
|
|
|
|
-- synchronni pamet RAM
|
|
DATA_ADDR : out std_logic_vector(12 downto 0); -- adresa do pameti
|
|
DATA_WDATA : out std_logic_vector(7 downto 0); -- mem[DATA_ADDR] <- DATA_WDATA pokud DATA_EN='1'
|
|
DATA_RDATA : in std_logic_vector(7 downto 0); -- DATA_RDATA <- ram[DATA_ADDR] pokud DATA_EN='1'
|
|
DATA_RDWR : out std_logic; -- cteni (1) / zapis (0)
|
|
DATA_EN : out std_logic; -- povoleni cinnosti
|
|
|
|
-- vstupni port
|
|
IN_DATA : in std_logic_vector(7 downto 0); -- IN_DATA <- stav klavesnice pokud IN_VLD='1' a IN_REQ='1'
|
|
IN_VLD : in std_logic; -- data platna
|
|
IN_REQ : out std_logic; -- pozadavek na vstup data
|
|
|
|
-- vystupni port
|
|
OUT_DATA : out std_logic_vector(7 downto 0); -- zapisovana data
|
|
OUT_BUSY : in std_logic; -- LCD je zaneprazdnen (1), nelze zapisovat
|
|
OUT_INV : out std_logic; -- pozadavek na aktivaci inverzniho zobrazeni (1)
|
|
OUT_WE : out std_logic; -- LCD <- OUT_DATA pokud OUT_WE='1' a OUT_BUSY='0'
|
|
|
|
-- stavove signaly
|
|
READY : out std_logic; -- hodnota 1 znamena, ze byl procesor inicializovan a zacina vykonavat program
|
|
DONE : out std_logic -- hodnota 1 znamena, ze procesor ukoncil vykonavani programu (narazil na instrukci halt)
|
|
);
|
|
end cpu;
|
|
|
|
|
|
-- ----------------------------------------------------------------------------
|
|
-- Architecture declaration
|
|
-- ----------------------------------------------------------------------------
|
|
architecture behavioral of cpu is
|
|
|
|
-- ASCII-based instruction set constants for BrainFuck language implementation
|
|
-- Each constant represents a specific command in the instruction set
|
|
constant INST_PTR_INC : std_logic_vector(7 downto 0) := X"3E"; -- > : Move data pointer right
|
|
constant INST_PTR_DEC : std_logic_vector(7 downto 0) := X"3C"; -- < : Move data pointer left
|
|
constant INST_VAL_INC : std_logic_vector(7 downto 0) := X"2B"; -- + : Increment value at pointer
|
|
constant INST_VAL_DEC : std_logic_vector(7 downto 0) := X"2D"; -- - : Decrement value at pointer
|
|
constant INST_WHILE_START : std_logic_vector(7 downto 0) := X"5B"; -- [ : Start loop if value != 0
|
|
constant INST_WHILE_END : std_logic_vector(7 downto 0) := X"5D"; -- ] : End loop if value != 0
|
|
constant INST_WRITE : std_logic_vector(7 downto 0) := X"2E"; -- . : Output value at pointer
|
|
constant INST_READ : std_logic_vector(7 downto 0) := X"2C"; -- , : Input value to pointer
|
|
constant INST_STORE : std_logic_vector(7 downto 0) := X"24"; -- $ : Store value to temp register
|
|
constant INST_LOAD : std_logic_vector(7 downto 0) := X"21"; -- ! : Load value from temp register
|
|
constant INST_HALT : std_logic_vector(7 downto 0) := X"40"; -- @ : Halt program execution
|
|
|
|
-- Finite State Machine (FSM) states definition
|
|
-- These states control the CPU's operation sequence
|
|
type fsm_state is (
|
|
s_idle, -- Initial state: CPU is inactive
|
|
s_fetch, -- Fetch: Read next instruction from memory
|
|
s_decode, -- Decode: Interpret the fetched instruction
|
|
s_init_find, -- Initialization: Search for program start (@)
|
|
s_init_find_read, -- Initialization: Read memory during @ search
|
|
s_init_ptr, -- Initialization: Set up data pointer
|
|
s_ptr_inc, -- Execution: Increment data pointer
|
|
s_ptr_dec, -- Execution: Decrement data pointer
|
|
s_val_inc, -- Value Increment: Start
|
|
s_val_inc_2, -- Value Increment: Read current value
|
|
s_val_inc_3, -- Value Increment: Write back
|
|
s_val_dec, -- Value Decrement: Start
|
|
s_val_dec_2, -- Value Decrement: Read current value
|
|
s_val_dec_3, -- Value Decrement: Write back
|
|
s_store_val, -- Memory: Store value to temporary register
|
|
s_load_val, -- Memory: Load from temporary register
|
|
s_load_val_2, -- Memory: Complete load operation
|
|
s_while_start, -- Loop: Begin processing loop start
|
|
s_while_start_2, -- Loop: Check if we should enter loop
|
|
s_while_start_3, -- Loop: Find matching end bracket
|
|
s_while_start_4, -- Loop: Forward navigation state
|
|
s_while_start_5, -- Loop: Process navigation
|
|
s_while_start_6, -- Loop: Handle nested loops (forward)
|
|
s_while_end, -- Loop: Begin processing loop end
|
|
s_while_end_2, -- Loop: Check if we should exit loop
|
|
s_while_end_3, -- Loop: Find matching start bracket
|
|
s_while_end_4, -- Loop: Backward navigation state
|
|
s_while_end_5, -- Loop: Process backward navigation
|
|
s_while_end_6, -- Loop: Handle nested loops (backward)
|
|
s_read_wait, -- I/O: Wait for input data
|
|
s_read_store, -- I/O: Store input data
|
|
s_write_wait, -- I/O: Wait for output ready
|
|
s_write, -- I/O: Perform write operation
|
|
s_halt -- Program End: Halt execution
|
|
);
|
|
|
|
-- Register and Control Signal Declarations
|
|
-- Program Counter (PC) related signals
|
|
signal pc_reg : std_logic_vector(12 downto 0) := (others => '0'); -- Stores instruction address
|
|
signal pc_inc : std_logic := '0'; -- Increment PC
|
|
signal pc_dec : std_logic := '0'; -- Decrement PC
|
|
signal pc_clear : std_logic := '0'; -- Reset PC to zero
|
|
|
|
-- Data Pointer (PTR) related signals
|
|
signal ptr_reg : std_logic_vector(12 downto 0) := (others => '0'); -- Stores data memory address
|
|
signal ptr_inc_i : std_logic := '0'; -- Increment PTR
|
|
signal ptr_dec_i : std_logic := '0'; -- Decrement PTR
|
|
signal ptr_pc_load : std_logic := '0'; -- Load PC value into PTR
|
|
|
|
-- Loop Counter (CNT) related signals
|
|
signal cnt_reg : std_logic_vector(7 downto 0) := (others => '0'); -- Tracks nested loop depth
|
|
signal cnt_inc : std_logic := '0'; -- Increment counter
|
|
signal cnt_dec : std_logic := '0'; -- Decrement counter
|
|
signal cnt_clear : std_logic := '0'; -- Reset counter
|
|
|
|
-- Temporary Storage Register
|
|
signal tmp_reg : std_logic_vector(7 downto 0) := (others => '0'); -- Temporary data storage
|
|
signal tmp_ld : std_logic := '0'; -- Load control for tmp_reg
|
|
|
|
-- FSM State Registers
|
|
signal pstate : fsm_state := s_idle; -- Current state
|
|
signal nstate : fsm_state := s_idle; -- Next state
|
|
|
|
-- Multiplexer Control Signals
|
|
signal mx1_sel : std_logic := '0'; -- Address MUX (0: PC, 1: PTR)
|
|
signal mx2_sel : std_logic_vector(1 downto 0) := "00"; -- Data MUX selection
|
|
-- 00: Input data
|
|
-- 01: Decremented value
|
|
-- 10: Incremented value
|
|
-- 11: Temporary register
|
|
|
|
begin
|
|
-- Program Counter (PC) Register Process
|
|
-- Controls program execution flow by managing instruction address
|
|
pc_reg_proc: process (CLK, RESET)
|
|
begin
|
|
if RESET = '1' then
|
|
-- Synchronous reset: Initialize PC to 0
|
|
pc_reg <= (others => '0');
|
|
elsif rising_edge(CLK) then
|
|
if EN = '1' then -- Only update when CPU is enabled
|
|
if pc_clear = '1' then
|
|
-- Clear operation takes precedence
|
|
pc_reg <= (others => '0');
|
|
elsif pc_inc = '1' then
|
|
-- Increment PC for next instruction
|
|
pc_reg <= pc_reg + 1;
|
|
elsif pc_dec = '1' then
|
|
-- Decrement PC (used in loop operations)
|
|
pc_reg <= pc_reg - 1;
|
|
end if;
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
-- Data Pointer (PTR) Register Process
|
|
-- Manages data memory addressing with wraparound functionality
|
|
ptr_reg_proc: process (CLK, RESET)
|
|
begin
|
|
if RESET = '1' then
|
|
-- Synchronous reset: Initialize PTR to 0
|
|
ptr_reg <= (others => '0');
|
|
elsif rising_edge(CLK) then
|
|
if EN = '1' then -- Only update when CPU is enabled
|
|
if ptr_pc_load = '1' then
|
|
-- Load PC value into PTR (used during initialization)
|
|
ptr_reg <= pc_reg;
|
|
elsif ptr_inc_i = '1' then
|
|
-- Increment with wraparound at maximum value
|
|
if ptr_reg = "1111111111111" then
|
|
ptr_reg <= (others => '0');
|
|
else
|
|
ptr_reg <= ptr_reg + 1;
|
|
end if;
|
|
elsif ptr_dec_i = '1' then
|
|
-- Decrement with wraparound at zero
|
|
if ptr_reg = "0000000000000" then
|
|
ptr_reg <= "1111111111111";
|
|
else
|
|
ptr_reg <= ptr_reg - 1;
|
|
end if;
|
|
end if;
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
-- Loop Counter (CNT) Register Process
|
|
-- Manages nested loop depth tracking
|
|
cnt_reg_proc: process (CLK, RESET)
|
|
begin
|
|
if RESET = '1' then
|
|
-- Synchronous reset: Initialize counter to 0
|
|
cnt_reg <= (others => '0');
|
|
elsif rising_edge(CLK) then
|
|
if EN = '1' then -- Only update when CPU is enabled
|
|
if cnt_clear = '1' then
|
|
-- Clear counter (start of new loop processing)
|
|
cnt_reg <= (others => '0');
|
|
elsif cnt_inc = '1' then
|
|
-- Increment counter (nested loop entry)
|
|
cnt_reg <= cnt_reg + 1;
|
|
elsif cnt_dec = '1' and cnt_reg /= "00000000" then
|
|
-- Decrement counter (nested loop exit)
|
|
-- Only decrement if not already zero
|
|
cnt_reg <= cnt_reg - 1;
|
|
end if;
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
-- Temporary (TMP) Register Process
|
|
-- Provides temporary storage for data values
|
|
tmp_reg_proc: process (CLK, RESET)
|
|
begin
|
|
if RESET = '1' then
|
|
-- Synchronous reset: Clear temporary storage
|
|
tmp_reg <= (others => '0');
|
|
elsif rising_edge(CLK) then
|
|
if EN = '1' and tmp_ld = '1' then
|
|
-- Load new value from data memory when enabled
|
|
tmp_reg <= DATA_RDATA;
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
-- FSM Present State Register Process
|
|
-- Manages state transitions in the CPU's control unit
|
|
fsm_pstate: process (CLK, RESET)
|
|
begin
|
|
if RESET = '1' then
|
|
-- Synchronous reset: Return to idle state
|
|
pstate <= s_idle;
|
|
elsif rising_edge(CLK) then
|
|
if EN = '1' then
|
|
-- Update current state with next state
|
|
pstate <= nstate;
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
-- FSM Next State Logic & Output Logic
|
|
-- This process handles:
|
|
-- 1. State transitions based on current state and inputs
|
|
-- 2. Control signal generation for each state
|
|
-- 3. Output signal management
|
|
fsm_nstate: process (pstate, DATA_RDATA, IN_VLD, OUT_BUSY, cnt_reg, EN)
|
|
begin
|
|
-- Default signal assignments to prevent latches
|
|
-- Control signals are set to inactive by default
|
|
nstate <= pstate; -- Maintain current state unless changed
|
|
pc_inc <= '0'; -- No PC increment
|
|
pc_dec <= '0'; -- No PC decrement
|
|
pc_clear <= '0'; -- No PC reset
|
|
ptr_inc_i <= '0'; -- No PTR increment
|
|
ptr_dec_i <= '0'; -- No PTR decrement
|
|
ptr_pc_load <= '0'; -- No PTR loading from PC
|
|
cnt_inc <= '0'; -- No counter increment
|
|
cnt_dec <= '0'; -- No counter decrement
|
|
cnt_clear <= '0'; -- No counter reset
|
|
tmp_ld <= '0'; -- No temporary register load
|
|
mx1_sel <= '0'; -- Address MUX defaults to PC
|
|
mx2_sel <= "00"; -- Data MUX defaults to input data
|
|
DATA_EN <= '0'; -- Memory access disabled
|
|
DATA_RDWR <= '1'; -- Default to read operation
|
|
IN_REQ <= '0'; -- No input request
|
|
OUT_WE <= '0'; -- No output write
|
|
OUT_DATA <= (others => '0'); -- Clear output data
|
|
DONE <= '0'; -- Not done by default
|
|
|
|
-- READY signal logic
|
|
-- Only inactive during initialization states
|
|
if pstate = s_idle or pstate = s_init_find or pstate = s_init_find_read then
|
|
READY <= '0';
|
|
else
|
|
READY <= '1';
|
|
end if;
|
|
|
|
-- Main FSM state machine
|
|
case pstate is
|
|
-- Initial state: Wait for enable signal
|
|
when s_idle =>
|
|
if EN = '1' then
|
|
nstate <= s_init_find; -- Start initialization
|
|
DATA_EN <= '1'; -- Enable memory access
|
|
DATA_RDWR <= '1'; -- Set to read mode
|
|
end if;
|
|
|
|
-- Initialization: Search for program start marker (@)
|
|
when s_init_find =>
|
|
DATA_EN <= '1'; -- Enable memory access
|
|
DATA_RDWR <= '1'; -- Set to read mode
|
|
nstate <= s_init_find_read; -- Move to read state
|
|
|
|
-- Read during initialization
|
|
when s_init_find_read =>
|
|
DATA_EN <= '1';
|
|
DATA_RDWR <= '1';
|
|
if DATA_RDATA = INST_HALT then -- Found @ symbol
|
|
nstate <= s_init_ptr;
|
|
ptr_pc_load <= '1'; -- Save program start position
|
|
else
|
|
pc_inc <= '1'; -- Continue searching
|
|
nstate <= s_init_find;
|
|
end if;
|
|
|
|
-- Initialize data pointer
|
|
when s_init_ptr =>
|
|
ptr_inc_i <= '1'; -- Move past program in memory
|
|
pc_clear <= '1'; -- Reset PC to start of program
|
|
nstate <= s_fetch; -- Begin program execution
|
|
|
|
-- Fetch cycle: Read next instruction
|
|
when s_fetch =>
|
|
DATA_EN <= '1'; -- Enable memory access
|
|
DATA_RDWR <= '1'; -- Set to read mode
|
|
nstate <= s_decode; -- Move to decode state
|
|
|
|
-- Decode cycle: Interpret instruction
|
|
when s_decode =>
|
|
DATA_EN <= '1';
|
|
DATA_RDWR <= '1';
|
|
|
|
-- Instruction decoding logic
|
|
case DATA_RDATA is
|
|
when INST_PTR_INC => -- > : Move pointer right
|
|
nstate <= s_ptr_inc;
|
|
when INST_PTR_DEC => -- < : Move pointer left
|
|
nstate <= s_ptr_dec;
|
|
when INST_VAL_INC => -- + : Increment value
|
|
nstate <= s_val_inc;
|
|
mx1_sel <= '1'; -- Select PTR address
|
|
when INST_VAL_DEC => -- - : Decrement value
|
|
nstate <= s_val_dec;
|
|
mx1_sel <= '1'; -- Select PTR address
|
|
when INST_WRITE => -- . : Output value
|
|
nstate <= s_write_wait;
|
|
mx1_sel <= '1'; -- Select PTR address
|
|
when INST_READ => -- , : Input value
|
|
nstate <= s_read_wait;
|
|
IN_REQ <= '1'; -- Request input
|
|
when INST_STORE => -- $ : Store to temp
|
|
nstate <= s_store_val;
|
|
mx1_sel <= '1'; -- Select PTR address
|
|
when INST_LOAD => -- ! : Load from temp
|
|
nstate <= s_load_val;
|
|
mx1_sel <= '1'; -- Select PTR address
|
|
when INST_WHILE_START => -- [ : Loop start
|
|
nstate <= s_while_start;
|
|
mx1_sel <= '1'; -- Select PTR address
|
|
when INST_WHILE_END => -- ] : Loop end
|
|
nstate <= s_while_end;
|
|
mx1_sel <= '1'; -- Select PTR address
|
|
when INST_HALT => -- @ : Halt program
|
|
nstate <= s_halt;
|
|
when others => -- Invalid/ignore
|
|
nstate <= s_fetch;
|
|
pc_inc <= '1'; -- Skip invalid instruction
|
|
end case;
|
|
|
|
-- Pointer increment operation
|
|
when s_ptr_inc =>
|
|
ptr_inc_i <= '1'; -- Increment data pointer
|
|
pc_inc <= '1'; -- Move to next instruction
|
|
nstate <= s_fetch; -- Return to fetch cycle
|
|
|
|
-- Pointer decrement operation
|
|
when s_ptr_dec =>
|
|
ptr_dec_i <= '1'; -- Decrement data pointer
|
|
pc_inc <= '1'; -- Move to next instruction
|
|
nstate <= s_fetch; -- Return to fetch cycle
|
|
|
|
-- Value increment operation (3-stage)
|
|
when s_val_inc =>
|
|
DATA_EN <= '1'; -- Enable memory
|
|
DATA_RDWR <= '1'; -- Read mode
|
|
mx1_sel <= '1'; -- Use PTR address
|
|
nstate <= s_val_inc_2;
|
|
|
|
when s_val_inc_2 =>
|
|
DATA_EN <= '1';
|
|
DATA_RDWR <= '1'; -- Still reading
|
|
mx1_sel <= '1'; -- Still using PTR
|
|
nstate <= s_val_inc_3;
|
|
|
|
when s_val_inc_3 =>
|
|
DATA_EN <= '1';
|
|
DATA_RDWR <= '0'; -- Switch to write mode
|
|
mx1_sel <= '1'; -- Still using PTR
|
|
mx2_sel <= "10"; -- Select incremented value
|
|
pc_inc <= '1'; -- Next instruction
|
|
nstate <= s_fetch;
|
|
|
|
-- Value decrement operation (3-stage)
|
|
when s_val_dec =>
|
|
DATA_EN <= '1'; -- Enable memory
|
|
DATA_RDWR <= '1'; -- Read mode
|
|
mx1_sel <= '1'; -- Use PTR address
|
|
nstate <= s_val_dec_2;
|
|
|
|
when s_val_dec_2 =>
|
|
DATA_EN <= '1';
|
|
DATA_RDWR <= '1'; -- Still reading
|
|
mx1_sel <= '1'; -- Still using PTR
|
|
nstate <= s_val_dec_3;
|
|
|
|
when s_val_dec_3 =>
|
|
DATA_EN <= '1';
|
|
DATA_RDWR <= '0'; -- Switch to write mode
|
|
mx1_sel <= '1'; -- Still using PTR
|
|
mx2_sel <= "01"; -- Select decremented value
|
|
pc_inc <= '1'; -- Next instruction
|
|
nstate <= s_fetch;
|
|
|
|
-- Store value to temporary register
|
|
when s_store_val =>
|
|
DATA_EN <= '1'; -- Enable memory
|
|
DATA_RDWR <= '1'; -- Read mode
|
|
tmp_ld <= '1'; -- Enable temp register load
|
|
pc_inc <= '1'; -- Next instruction
|
|
nstate <= s_fetch;
|
|
|
|
-- Load value from temporary register
|
|
when s_load_val =>
|
|
DATA_EN <= '1'; -- Enable memory
|
|
DATA_RDWR <= '0'; -- Write mode
|
|
mx1_sel <= '1'; -- Use PTR address
|
|
mx2_sel <= "11"; -- Select temp register value
|
|
pc_inc <= '1'; -- Next instruction
|
|
nstate <= s_fetch;
|
|
|
|
-- Input handling states
|
|
when s_read_wait =>
|
|
IN_REQ <= '1'; -- Request input
|
|
if IN_VLD = '1' then -- Input data valid
|
|
nstate <= s_read_store;
|
|
end if;
|
|
|
|
when s_read_store =>
|
|
DATA_EN <= '1'; -- Enable memory
|
|
DATA_RDWR <= '0'; -- Write mode
|
|
mx1_sel <= '1'; -- Use PTR address
|
|
mx2_sel <= "00"; -- Select input data
|
|
pc_inc <= '1'; -- Next instruction
|
|
nstate <= s_fetch;
|
|
|
|
-- Output handling states
|
|
when s_write_wait =>
|
|
DATA_EN <= '1'; -- Enable memory
|
|
DATA_RDWR <= '1'; -- Read mode
|
|
mx1_sel <= '1'; -- Use PTR address
|
|
if OUT_BUSY = '0' then -- Output ready
|
|
nstate <= s_write;
|
|
end if;
|
|
|
|
when s_write =>
|
|
OUT_WE <= '1'; -- Write to output
|
|
OUT_DATA <= DATA_RDATA; -- Output current value
|
|
pc_inc <= '1'; -- Next instruction
|
|
nstate <= s_fetch;
|
|
|
|
-- Loop start handling states
|
|
when s_while_start =>
|
|
DATA_EN <= '1'; -- Enable memory
|
|
DATA_RDWR <= '1'; -- Read mode
|
|
mx1_sel <= '1'; -- Use PTR address
|
|
nstate <= s_while_start_2;
|
|
|
|
when s_while_start_2 =>
|
|
DATA_EN <= '1';
|
|
DATA_RDWR <= '1';
|
|
mx1_sel <= '1';
|
|
nstate <= s_while_start_3;
|
|
|
|
when s_while_start_3 =>
|
|
if DATA_RDATA = "00000000" then -- Skip loop if value is 0
|
|
pc_inc <= '1';
|
|
cnt_clear <= '1';
|
|
nstate <= s_while_start_4;
|
|
else
|
|
pc_inc <= '1'; -- Enter loop
|
|
nstate <= s_fetch;
|
|
end if;
|
|
|
|
-- Loop start bracket matching states
|
|
when s_while_start_4 =>
|
|
DATA_EN <= '1';
|
|
DATA_RDWR <= '1';
|
|
pc_inc <= '1';
|
|
nstate <= s_while_start_5;
|
|
|
|
when s_while_start_5 =>
|
|
DATA_EN <= '1';
|
|
DATA_RDWR <= '1';
|
|
nstate <= s_while_start_6;
|
|
|
|
when s_while_start_6 =>
|
|
if DATA_RDATA = INST_WHILE_START then -- Found nested [
|
|
cnt_inc <= '1'; -- Increment nest level
|
|
nstate <= s_while_start_4;
|
|
elsif DATA_RDATA = INST_WHILE_END then -- Found ]
|
|
if cnt_reg = "00000000" then -- Matching ] found
|
|
pc_inc <= '1';
|
|
nstate <= s_fetch;
|
|
else -- Nested ] found
|
|
cnt_dec <= '1';
|
|
nstate <= s_while_start_4;
|
|
end if;
|
|
else
|
|
nstate <= s_while_start_4; -- Keep searching
|
|
end if;
|
|
|
|
-- Loop end handling states
|
|
when s_while_end =>
|
|
DATA_EN <= '1'; -- Enable memory
|
|
DATA_RDWR <= '1'; -- Read mode
|
|
mx1_sel <= '1'; -- Use PTR address
|
|
nstate <= s_while_end_2;
|
|
|
|
when s_while_end_2 =>
|
|
DATA_EN <= '1';
|
|
DATA_RDWR <= '1';
|
|
mx1_sel <= '1';
|
|
nstate <= s_while_end_3;
|
|
|
|
when s_while_end_3 =>
|
|
if DATA_RDATA /= "00000000" then -- Continue loop if value not 0
|
|
pc_dec <= '1';
|
|
cnt_clear <= '1';
|
|
nstate <= s_while_end_4;
|
|
else
|
|
pc_inc <= '1'; -- Exit loop
|
|
nstate <= s_fetch;
|
|
end if;
|
|
|
|
-- Loop end bracket matching states
|
|
when s_while_end_4 =>
|
|
DATA_EN <= '1';
|
|
DATA_RDWR <= '1';
|
|
pc_dec <= '1'; -- Search backwards
|
|
nstate <= s_while_end_5;
|
|
|
|
when s_while_end_5 =>
|
|
DATA_EN <= '1';
|
|
DATA_RDWR <= '1';
|
|
nstate <= s_while_end_6;
|
|
|
|
when s_while_end_6 =>
|
|
if DATA_RDATA = INST_WHILE_END then -- Found nested ]
|
|
cnt_inc <= '1'; -- Increment nest level
|
|
nstate <= s_while_end_4;
|
|
elsif DATA_RDATA = INST_WHILE_START then -- Found [
|
|
if cnt_reg = "00000000" then -- Matching [ found
|
|
pc_inc <= '1';
|
|
nstate <= s_fetch;
|
|
else -- Nested [ found
|
|
cnt_dec <= '1';
|
|
nstate <= s_while_end_4;
|
|
end if;
|
|
else
|
|
nstate <= s_while_end_4; -- Keep searching
|
|
end if;
|
|
|
|
-- Program termination state
|
|
when s_halt =>
|
|
DONE <= '1'; -- Signal program completion
|
|
nstate <= s_halt; -- Stay in halt state
|
|
|
|
-- Fallback state (shouldn't occur)
|
|
when others =>
|
|
nstate <= s_idle; -- Return to idle state
|
|
|
|
end case;
|
|
end process;
|
|
|
|
-- Output signal assignments
|
|
OUT_INV <= '0'; -- Display is always in normal mode
|
|
|
|
-- Address Multiplexer (MX1)
|
|
-- Selects between program counter (PC) and data pointer (PTR)
|
|
DATA_ADDR <= ptr_reg when mx1_sel = '1' else pc_reg;
|
|
|
|
-- Data Multiplexer (MX2)
|
|
-- Selects between different data sources based on operation
|
|
DATA_WDATA <= IN_DATA when mx2_sel = "00" else -- Input data
|
|
DATA_RDATA - 1 when mx2_sel = "01" else -- Decremented value
|
|
DATA_RDATA + 1 when mx2_sel = "10" else -- Incremented value
|
|
tmp_reg; -- Temporary register
|
|
|
|
end behavioral; |