summaryrefslogtreecommitdiffstats
path: root/hdl
diff options
context:
space:
mode:
authorVaino Kauppila <vaino@vke.fi>2026-04-19 12:32:58 +0300
committerVaino Kauppila <vaino@vke.fi>2026-04-19 12:32:58 +0300
commiteb8643ddbef43547eb7bfe58789a3908926c7258 (patch)
treefadfba88a1c71b266b8c67df166a3a6ead6b30d1 /hdl
parent129a1c975dbd0c042576010b1f6eb40d19fc5733 (diff)
downloadvhdl_pic-eb8643ddbef43547eb7bfe58789a3908926c7258.tar.gz
vhdl_pic-eb8643ddbef43547eb7bfe58789a3908926c7258.zip
refactor dir structure, add fib.c
Diffstat (limited to 'hdl')
-rw-r--r--hdl/ALU.vhd216
-rw-r--r--hdl/PIC.vhd181
-rw-r--r--hdl/common.vhd10
-rw-r--r--hdl/decoder.vhd155
-rw-r--r--hdl/dpram.vhd110
-rw-r--r--hdl/fifo.vhd75
-rw-r--r--hdl/hdl-prj.json24
-rw-r--r--hdl/hexfile_reader.vhd342
-rw-r--r--hdl/pic_top.vhd81
-rw-r--r--hdl/stack.vhd51
-rw-r--r--hdl/state_machine.vhd230
-rw-r--r--hdl/tb_pic.vhd83
-rw-r--r--hdl/tb_stack.vhd121
-rw-r--r--hdl/uart.vhd195
14 files changed, 1874 insertions, 0 deletions
diff --git a/hdl/ALU.vhd b/hdl/ALU.vhd
new file mode 100644
index 0000000..b7c0344
--- /dev/null
+++ b/hdl/ALU.vhd
@@ -0,0 +1,216 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+use work.alu_types.all;
+
+entity alu is
+ port (
+ a, b : in std_logic_vector(7 downto 0); -- inputs to ALU. In
+ -- general here 'a' is
+ -- a value from working
+ -- register W and 'b'
+ -- is from a file
+ -- register.
+
+ op : in alu_op; -- ALU operation which
+ -- one to use
+
+ bit_select : in std_logic_vector(2 downto 0); -- this is the 'b' signal in
+ -- the datasheet.
+
+ status_in : in std_logic_vector(2 downto 0); -- status before ALU operation.
+
+ status : out std_logic_vector(2 downto 0); -- status after ALU operation.
+
+ result : out std_logic_vector(7 downto 0); -- result of the ALU
+ -- operation. This is
+ -- then either stored
+ -- in file register f
+ -- or work register W.
+
+
+ skip : out std_logic -- SKIP signal.
+
+ -- the status register is of form:
+ -- IRP RP1 RP0 ~TO ~PD Z DC C
+ -- 7 6 5 4 3 2 1 0
+ -- ALU only operates on 3 least significant bits.
+ );
+
+end entity alu;
+
+architecture rtl of alu is
+
+ -- ternary op; returns x1 if cond is true, x2 otherwise.
+ pure function sel(cond : boolean; x1, x2 : std_logic) return std_logic is
+ begin
+ if cond then return x1; else return x2; end if;
+ end function;
+
+ -- return status with the given flag set to val, all other bits same
+ pure function set_flag(flag : alu_flag; val : std_logic; stat : std_logic_vector(2 downto 0)) return std_logic_vector is
+ variable s_out : std_logic_vector(2 downto 0) := stat;
+ begin
+ case flag is
+ when FLAG_Z => s_out(2) := val;
+ when FLAG_DC => s_out(1) := val;
+ when FLAG_C => s_out(0) := val;
+ end case;
+ return s_out;
+ end function;
+
+ -- Computes val +/- 1, drives result and either sets Z flag or skip signal.
+ procedure inc_dec(
+ val : in std_logic_vector(7 downto 0);
+ inc : in boolean; -- true = increment, false = decrement
+ do_skip : in boolean; -- true for *FSZ variants
+ signal res : out std_logic_vector(7 downto 0);
+ signal stat : out std_logic_vector(2 downto 0);
+ signal do_skip_out : out std_logic;
+ stat_in : in std_logic_vector(2 downto 0)
+ ) is
+ variable t : std_logic_vector(7 downto 0);
+ begin
+ if inc then
+ t := std_logic_vector(unsigned(val) + 1);
+ else
+ t := std_logic_vector(unsigned(val) - 1);
+ end if;
+ res <= t;
+ if do_skip then
+ if unsigned(t) = 0 then
+ do_skip_out <= '1';
+ else
+ do_skip_out <= '0';
+ end if;
+ else
+ stat <= set_flag(FLAG_Z, sel(unsigned(t) = 0, '1', '0'), stat_in);
+ end if;
+ end procedure;
+
+begin
+ process(a, b, op, bit_select, status_in)
+ variable tmp : std_logic_vector(7 downto 0);
+ variable tmp_extended : std_logic_vector(8 downto 0);
+
+ variable tmp_status : std_logic_vector(2 downto 0);
+ begin
+
+ -- DEFAULT case (NOP)
+ result <= (others => '0'); -- default: zero result
+ skip <= '0'; -- default: no skip
+ status <= status_in; -- default: pass status thru
+
+ case op is
+ when NOP => -- nothing
+
+
+ when ADDWF | ADDLW =>
+ -- addition
+ tmp_extended := std_logic_vector(unsigned('0' & a) + unsigned('0' & b));
+
+ -- DC
+ tmp(4 downto 0) := std_logic_vector(('0' & unsigned(a(3 downto 0))) + ('0' & unsigned(b(3 downto 0))));
+
+ -- flags
+ tmp_status := set_flag(FLAG_Z, sel(unsigned(tmp_extended(7 downto 0)) = 0, '1', '0'), status_in);
+ tmp_status := set_flag(FLAG_C, tmp_extended(8), tmp_status);
+ tmp_status := set_flag(FLAG_DC, tmp(4), tmp_status);
+
+ status <= tmp_status;
+ result <= tmp_extended(7 downto 0);
+
+ when SUBLW | SUBWF =>
+ -- PIC16F84A: SUBWF = f - W, SUBLW = k - W → dest = b - a
+ tmp_extended := std_logic_vector(unsigned('0' & b) - unsigned('0' & a));
+
+ -- DC: borrow out of lower nibble
+ tmp(4 downto 0) := std_logic_vector(('0' & unsigned(b(3 downto 0))) - ('0' & unsigned(a(3 downto 0))));
+
+ tmp_status := set_flag(FLAG_Z, sel(unsigned(tmp_extended(7 downto 0)) = 0, '1', '0'), status_in);
+ tmp_status := set_flag(FLAG_C, not tmp_extended(8), tmp_status);
+ tmp_status := set_flag(FLAG_DC, not tmp(4), tmp_status);
+
+ status <= tmp_status;
+ result <= tmp_extended(7 downto 0);
+
+ when IORLW | IORWF =>
+ tmp := a or b;
+ result <= tmp;
+ status <= set_flag(FLAG_Z, sel(unsigned(tmp) = 0, '1', '0'), status_in);
+
+ when ANDWF | ANDLW =>
+ tmp := a and b;
+ result <= tmp;
+ status <= set_flag(FLAG_Z, sel(unsigned(tmp) = 0, '1', '0'), status_in);
+
+ when XORLW | XORWF =>
+ tmp := a xor b;
+ result <= tmp;
+ status <= set_flag(FLAG_Z, sel(unsigned(tmp) = 0, '1', '0'), status_in);
+
+ when COMF =>
+ tmp := not b;
+ result <= tmp;
+ status <= set_flag(FLAG_Z, sel(unsigned(tmp) = 0, '1', '0'), status_in);
+
+ when DECF => inc_dec(b, false, false, result, status, skip, status_in);
+ when DECFSZ => inc_dec(b, false, true, result, status, skip, status_in);
+ when INCF => inc_dec(b, true, false, result, status, skip, status_in);
+ when INCFSZ => inc_dec(b, true, true, result, status, skip, status_in);
+
+ when RLF =>
+ tmp := std_logic_vector(shift_left(unsigned(b), 1));
+ tmp(0) := status_in(0);
+ status(0) <= b(7);
+ result <= tmp;
+
+ when RRF =>
+ tmp := std_logic_vector(shift_right(unsigned(b), 1));
+ tmp(7) := status_in(0);
+ status(0) <= b(0);
+ result <= tmp;
+
+
+
+ when BCF =>
+ result <= b;
+ result(to_integer(unsigned(bit_select))) <= '0';
+
+ when BTFSC =>
+ skip <= not b(to_integer(unsigned(bit_select)));
+
+ when BSF =>
+ result <= b;
+ result(to_integer(unsigned(bit_select))) <= '1';
+
+ when BTFSS =>
+ skip <= b(to_integer(unsigned(bit_select)));
+
+ when SWAPF =>
+ tmp(7 downto 4) := b(3 downto 0);
+ tmp(3 downto 0) := b(7 downto 4);
+ result <= tmp;
+
+
+ when CLRF | CLRW =>
+ result <= (others => '0');
+ status <= set_flag(FLAG_Z, '1', status_in);
+
+ when MOVF =>
+ result <= b;
+ status <= set_flag(FLAG_Z, sel(unsigned(b) = 0, '1', '0'), status_in);
+ when MOVWF => result <= a;
+ when MOVLW => result <= b;
+
+
+
+
+ when CALL => null;
+ when GOTO => null;
+ when RETLW => result <= b;
+ when RETUR => null;
+
+ end case;
+ end process;
+end architecture rtl;
diff --git a/hdl/PIC.vhd b/hdl/PIC.vhd
new file mode 100644
index 0000000..a31bec7
--- /dev/null
+++ b/hdl/PIC.vhd
@@ -0,0 +1,181 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+use work.alu_types.all;
+
+entity PIC is
+ generic (
+ CLK_FREQ : positive := 50_000_000; -- 50M, GW5A clock
+ BAUD_RATE : positive := 115_200
+ );
+ port (
+ clk : in std_logic;
+ reset : in std_logic;
+ opcode : in std_logic_vector(13 downto 0);
+
+ program_counter : out std_logic_vector(12 downto 0) := (others => '0');
+ instruction_return : out std_logic := '0';
+ work_reg : out std_logic_vector(7 downto 0) := (others => '0');
+ status : out std_logic_vector(2 downto 0) := (others => '0');
+ port_a : out std_logic_vector(7 downto 0) := (others => '0');
+ port_b : out std_logic_vector(7 downto 0) := (others => '0');
+
+ -- UART physical pins
+ uart_tx : out std_logic;
+ uart_rx : in std_logic
+ );
+end entity PIC;
+
+architecture rtl of PIC is
+ signal W : std_logic_vector(7 downto 0) := (others => '0');
+
+ signal alu_operation : alu_op;
+ signal alu_result : std_logic_vector(7 downto 0);
+ signal alu_skip : std_logic;
+
+ signal mem_q : std_logic_vector(7 downto 0);
+ signal mem_qstatus : std_logic_vector(2 downto 0);
+ signal mem_we : std_logic;
+ signal mem_re : std_logic;
+
+ signal bit_select : std_logic_vector(2 downto 0);
+ signal addr : std_logic_vector(6 downto 0);
+ signal sm_data : std_logic_vector(7 downto 0);
+ signal sm_pc : std_logic_vector(12 downto 0);
+ signal we_w : std_logic;
+ signal instr_ret : std_logic;
+ signal alu_status : std_logic_vector(2 downto 0);
+
+ signal use_literal : std_logic;
+ signal we_status : std_logic;
+ signal b_operand : std_logic_vector(7 downto 0);
+
+ signal stack_push : std_logic;
+ signal stack_pop : std_logic;
+ signal stack_din : std_logic_vector(12 downto 0);
+ signal stack_dout : std_logic_vector(12 downto 0);
+
+ -- UART <-> dpram bridge signals
+ signal uart_tx_byte : std_logic_vector(7 downto 0);
+ signal uart_tx_send : std_logic;
+ signal uart_tx_done : std_logic;
+ signal uart_rx_byte : std_logic_vector(7 downto 0);
+ signal uart_rx_rd_en : std_logic;
+ signal uart_rx_empty : std_logic;
+ signal uart_rx_full : std_logic;
+
+begin
+
+ work_reg <= W;
+ program_counter <= sm_pc;
+ instruction_return <= instr_ret;
+ status <= alu_status;
+
+ b_operand_select : process(use_literal, sm_data, mem_q)
+ begin
+ if use_literal = '1' then
+ b_operand <= sm_data;
+ else
+ b_operand <= mem_q;
+ end if;
+ end process;
+
+ w_reg : process(clk)
+ begin
+ if rising_edge(clk) then
+ if we_w = '1' then
+ W <= alu_result;
+ end if;
+ end if;
+ end process w_reg;
+
+ sm : entity work.state_machine(rtl)
+ port map (
+ clk => clk,
+ opcode => opcode,
+ reset => reset,
+ op => alu_operation,
+ we_mem => mem_we,
+ re_mem => mem_re,
+ we_w => we_w,
+ instr_ret => instr_ret,
+ bit_select => bit_select,
+ data => sm_data,
+ pc => sm_pc,
+ addr => addr,
+ use_literal => use_literal,
+ we_status => we_status,
+ stack_push => stack_push,
+ stack_pop => stack_pop,
+ stack_din => stack_din,
+ stack_dout => stack_dout,
+ alu_skip => alu_skip,
+ alu_result => alu_result
+ );
+
+ alu : entity work.alu(rtl)
+ port map (
+ a => W,
+ b => b_operand,
+ op => alu_operation,
+ bit_select => bit_select,
+ status_in => mem_qstatus,
+ result => alu_result,
+ status => alu_status,
+ skip => alu_skip
+ );
+
+ stk : entity work.stack(rtl)
+ port map (
+ clk => clk,
+ reset => reset,
+ push => stack_push,
+ pop => stack_pop,
+ din => stack_din,
+ dout => stack_dout
+ );
+
+ mem : entity work.dpram(rtl)
+ generic map (DEPTH => 128)
+ port map (
+ clk => clk,
+ we => mem_we,
+ re => mem_re,
+ d => alu_result,
+ d_status => "00000" & alu_status,
+ addr => addr,
+ q => mem_q,
+ q_status => mem_qstatus,
+ port_a => port_a,
+ port_b => port_b,
+ we_status => we_status,
+ uart_tx_byte => uart_tx_byte,
+ uart_tx_send => uart_tx_send,
+ uart_tx_done => uart_tx_done,
+ uart_rx_byte => uart_rx_byte,
+ uart_rx_rd_en => uart_rx_rd_en,
+ uart_rx_empty => uart_rx_empty,
+ uart_rx_full => uart_rx_full
+ );
+
+ uart_inst : entity work.uart
+ generic map (
+ CLK_FREQ => CLK_FREQ,
+ BAUD_RATE => BAUD_RATE,
+ CLKS_PER_BIT => CLK_FREQ / BAUD_RATE
+ )
+ port map (
+ clk => clk,
+ tx_should_send => uart_tx_send,
+ byte_in => uart_tx_byte,
+ tx_done => uart_tx_done,
+ baud_tick => open,
+ uart_tx => uart_tx,
+ uart_rx => uart_rx,
+ rd_en => uart_rx_rd_en,
+ rx_data => uart_rx_byte,
+ rx_empty => uart_rx_empty,
+ rx_full => uart_rx_full
+ );
+
+end architecture rtl;
diff --git a/hdl/common.vhd b/hdl/common.vhd
new file mode 100644
index 0000000..c986ec5
--- /dev/null
+++ b/hdl/common.vhd
@@ -0,0 +1,10 @@
+package alu_types is
+ type alu_op is (ADDWF, ANDWF, ADDLW, ANDLW, BCF, BTFSC,
+ BSF, BTFSS, CLRF, CLRW, COMF, DECF,
+ DECFSZ, INCF, INCFSZ, IORLW, MOVF, MOVWF, CALL,
+ GOTO, MOVLW, RETLW, RETUR, IORWF, NOP,
+ RLF, RRF, SUBLW, SUBWF, SWAPF, XORLW, XORWF);
+
+ -- Status register bits: Z=2, DC=1, C=0
+ type alu_flag is (FLAG_Z, FLAG_DC, FLAG_C);
+end package;
diff --git a/hdl/decoder.vhd b/hdl/decoder.vhd
new file mode 100644
index 0000000..5a1eb2c
--- /dev/null
+++ b/hdl/decoder.vhd
@@ -0,0 +1,155 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use work.alu_types.all;
+
+package decoder is
+ type instruction_class_t is (
+ BIT_OP,
+ BYTE_OP,
+ LITERAL_OP,
+ CONTROL_OP,
+ UNKNOWN
+ );
+
+ type instruction_t is record
+ class : instruction_class_t;
+ op : alu_op;
+ f : std_logic_vector(6 downto 0);
+ b : std_logic_vector(2 downto 0);
+ d : std_logic;
+ k : std_logic_vector(10 downto 0);
+ end record;
+
+ pure function instruction_get_class(opc : std_logic_vector(13 downto 0)) return instruction_class_t;
+
+ pure function instruction_get_operation(
+ opc : std_logic_vector(13 downto 0);
+ instr_class : instruction_class_t)
+ return alu_op;
+
+ pure function instruction_decode(opc : std_logic_vector(13 downto 0)) return instruction_t;
+
+end package;
+
+package body decoder is
+
+ pure function instruction_decode(opc : std_logic_vector(13 downto 0))
+ return instruction_t is
+ variable instr : instruction_t;
+ begin
+ instr.class := instruction_get_class(opc);
+ instr.op := instruction_get_operation(opc, instr.class);
+
+ instr.f := (others => '0');
+ instr.b := (others => '0');
+ instr.d := '0';
+ instr.k := (others => '0');
+
+ case instr.class is
+ when BIT_OP =>
+ instr.b := opc(9 downto 7);
+ instr.f := opc(6 downto 0);
+ when BYTE_OP =>
+ instr.f := opc(6 downto 0);
+ instr.d := opc(7);
+ when LITERAL_OP | CONTROL_OP =>
+ instr.k := opc(10 downto 0);
+ when UNKNOWN =>
+ end case;
+
+ return instr;
+ end function;
+
+ pure function instruction_get_class(opc : std_logic_vector(13 downto 0))
+ return instruction_class_t is
+ begin
+ -- RETURN (0x0008) has bits[13:12]="00" but must be treated as LITERAL_OP
+ if opc = "00000000001000" then return LITERAL_OP; end if;
+ case opc(13 downto 12) is
+ -- byte-oriented file-register operations
+ when "00" =>
+ return BYTE_OP;
+ when "01" =>
+ return BIT_OP;
+ when "10" =>
+ return CONTROL_OP;
+ when "11" =>
+ return LITERAL_OP;
+ when others =>
+ return UNKNOWN;
+ end case;
+ end function;
+
+ pure function instruction_get_operation(
+ opc : std_logic_vector(13 downto 0);
+ instr_class : instruction_class_t)
+ return alu_op is
+ begin
+ case instr_class is
+
+ when BYTE_OP =>
+ case opc(11 downto 8) is
+ when "0000" =>
+ if opc(7) = '1' then return MOVWF;
+ else return NOP;
+ end if;
+ when "0001" =>
+ if opc(7) = '1' then return CLRF;
+ else return CLRW;
+ end if;
+ when "0010" => return SUBWF;
+ when "0011" => return DECF;
+ when "0100" => return IORWF;
+ when "0101" => return ANDWF;
+ when "0110" => return XORWF;
+ when "0111" => return ADDWF;
+ when "1000" => return MOVF;
+ when "1001" => return COMF;
+ when "1010" => return INCF;
+ when "1011" => return DECFSZ;
+ when "1100" => return RRF;
+ when "1101" => return RLF;
+ when "1110" => return SWAPF;
+ when "1111" => return INCFSZ;
+ when others => return NOP;
+ end case;
+
+ when BIT_OP =>
+ case opc(11 downto 10) is
+ when "00" => return BCF;
+ when "01" => return BSF;
+ when "10" => return BTFSC;
+ when "11" => return BTFSS;
+ when others => return NOP;
+ end case;
+
+ when CONTROL_OP =>
+ if opc(11) = '1' then return GOTO;
+ else return CALL;
+ end if;
+
+ when LITERAL_OP =>
+ -- fixed full-word patterns first (fall inside 00xx space)
+ -- if opc(11 downto 0) = "000000000001" then return RETFIE;
+ if opc(11 downto 0) = "000000001000" then return RETUR;
+ -- elsif opc(11 downto 0) = "000000000011" then return SLEEP;
+ -- elsif opc(11 downto 0) = "000000000100" then return CLRWDT;
+ else
+ case? opc(11 downto 8) is
+ when "00--" => return MOVLW;
+ when "01--" => return RETLW;
+ when "1000" => return IORLW;
+ when "1001" => return ANDLW;
+ when "1010" => return XORLW;
+ when "110-" => return SUBLW;
+ when "111-" => return ADDLW;
+ when others => return NOP;
+ end case?;
+ end if;
+
+ when others => return NOP;
+
+ end case;
+ end function;
+
+end decoder;
diff --git a/hdl/dpram.vhd b/hdl/dpram.vhd
new file mode 100644
index 0000000..44d7adf
--- /dev/null
+++ b/hdl/dpram.vhd
@@ -0,0 +1,110 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+-- Data memory with UART memory-mapped registers.
+-- Addresses 0x70-0x72 are used (unimplemented in real PIC16F84A, no header conflict):
+-- 0x70 UART_TX write: send byte (held until tx_done)
+-- 0x71 UART_RX read: current FIFO head (FWFT), pulses rd_en to pop
+-- 0x72 UART_STATUS read: "00000" & rx_full & rx_empty & tx_busy
+
+entity dpram is
+ generic (DEPTH : positive := 128);
+ port (
+ clk : in std_logic;
+ we : in std_logic;
+ re : in std_logic;
+ d : in std_logic_vector(7 downto 0);
+ d_status : in std_logic_vector(7 downto 0);
+ addr : in std_logic_vector(6 downto 0);
+ q : out std_logic_vector(7 downto 0);
+ q_status : out std_logic_vector(2 downto 0);
+
+ port_a : out std_logic_vector(7 downto 0);
+ port_b : out std_logic_vector(7 downto 0);
+ we_status : in std_logic;
+
+ -- UART interface
+ uart_tx_byte : out std_logic_vector(7 downto 0) := (others => '0');
+ uart_tx_send : out std_logic := '0';
+ uart_tx_done : in std_logic;
+ uart_rx_byte : in std_logic_vector(7 downto 0);
+ uart_rx_rd_en : out std_logic := '0';
+ uart_rx_empty : in std_logic;
+ uart_rx_full : in std_logic
+ );
+end entity dpram;
+
+architecture rtl of dpram is
+ type mem_t is
+ array (0 to DEPTH - 1)
+ of std_logic_vector(7 downto 0);
+
+ signal mem : mem_t := (others => (others => '0'));
+ signal uart_tx_pending : std_logic := '0';
+
+begin
+ -- STATUS output is combinatorial
+ q_status <= mem(16#03#)(2 downto 0);
+
+ port_a <= mem(16#05#);
+ port_b <= mem(16#06#);
+
+ -- tx_send is high while a transmission is in progress
+ uart_tx_send <= uart_tx_pending;
+
+ -- SR: set when CPU writes to 0x08, clear when UART asserts tx_done
+ tx_ctrl : process(clk)
+ begin
+ if rising_edge(clk) then
+ if we = '1' and addr = "1110000" then -- 0x70 UART_TX
+ uart_tx_byte <= d;
+ uart_tx_pending <= '1';
+ elsif uart_tx_done = '1' then
+ uart_tx_pending <= '0';
+ end if;
+ end if;
+ end process tx_ctrl;
+
+ read : process(clk)
+ begin
+ if rising_edge(clk) then
+ uart_rx_rd_en <= '0'; -- default: no pop
+ if re = '1' then
+ if addr = "1110001" then -- 0x71 UART_RX
+ q <= uart_rx_byte; -- FWFT head, valid combinatorially
+ if uart_rx_empty = '0' then
+ uart_rx_rd_en <= '1'; -- advance FIFO pointer
+ end if;
+ elsif addr = "1110010" then -- 0x72 UART_STATUS
+ q <= "00000" & uart_rx_full & uart_rx_empty & uart_tx_pending;
+ else
+ q <= mem(to_integer(unsigned(addr)));
+ end if;
+ end if;
+ end if;
+ end process read;
+
+ write : process(clk)
+ begin
+ if rising_edge(clk) then
+ if we_status = '1' then
+ mem(16#03#) <= d_status;
+ end if;
+ if we = '1' then
+ if addr = "0000011" then -- 0x03 STATUS: direct write
+ mem(16#03#) <= d;
+ elsif addr = "1110000" or -- 0x70 UART_TX
+ addr = "1110001" or -- 0x71 UART_RX (read-only but handled gracefully)
+ addr = "1110010" then -- 0x72 UART_STATUS (read-only)
+ -- UART-mapped addresses: don't write to RAM; still update STATUS
+ mem(16#03#) <= d_status;
+ else
+ mem(16#03#) <= d_status;
+ mem(to_integer(unsigned(addr))) <= d;
+ end if;
+ end if;
+ end if;
+ end process write;
+
+end architecture rtl;
diff --git a/hdl/fifo.vhd b/hdl/fifo.vhd
new file mode 100644
index 0000000..9dd8523
--- /dev/null
+++ b/hdl/fifo.vhd
@@ -0,0 +1,75 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+-- First-word fall-through (FWFT) FIFO.
+-- dout always presents the current head combinatorially (no read latency).
+-- rd_en advances the read pointer; the next head is visible on the same cycle.
+-- DEPTH must be a power of 2; ADDR_BITS = log2(DEPTH).
+
+entity fifo is
+ generic (
+ DEPTH : positive := 16;
+ ADDR_BITS : positive := 4;
+ WIDTH : positive := 8
+ );
+ port (
+ clk : in std_logic;
+ wr_en : in std_logic;
+ rd_en : in std_logic;
+ din : in std_logic_vector(WIDTH - 1 downto 0);
+ dout : out std_logic_vector(WIDTH - 1 downto 0);
+ full : out std_logic;
+ empty : out std_logic
+ );
+end entity fifo;
+
+architecture rtl of fifo is
+
+ -- MSB pointer trick: pointers are ADDR_BITS+1 wide.
+ -- Empty: read_ptr = write_ptr
+ -- Full: (read_ptr XOR write_ptr) = DEPTH (MSBs differ, lower bits equal)
+ signal read_ptr : unsigned(ADDR_BITS downto 0) := (others => '0');
+ signal write_ptr : unsigned(ADDR_BITS downto 0) := (others => '0');
+
+ type mem_t is
+ array (0 to DEPTH - 1)
+ of std_logic_vector(WIDTH - 1 downto 0);
+
+ signal mem : mem_t := (others => (others => '0'));
+
+ signal empty_i : std_logic;
+ signal full_i : std_logic;
+
+begin
+
+ empty_i <= '1' when read_ptr = write_ptr else '0';
+ full_i <= '1' when (read_ptr xor write_ptr) = to_unsigned(DEPTH, ADDR_BITS + 1)
+ else '0';
+
+ empty <= empty_i;
+ full <= full_i;
+
+ -- FWFT: head is always visible without asserting rd_en
+ dout <= mem(to_integer(read_ptr(ADDR_BITS - 1 downto 0)));
+
+ enqueue : process(clk)
+ begin
+ if rising_edge(clk) then
+ if wr_en = '1' and full_i = '0' then
+ mem(to_integer(write_ptr(ADDR_BITS - 1 downto 0))) <= din;
+ write_ptr <= write_ptr + 1;
+ end if;
+ end if;
+ end process enqueue;
+
+ dequeue : process(clk)
+ begin
+ if rising_edge(clk) then
+ if rd_en = '1' and empty_i = '0' then
+ read_ptr <= read_ptr + 1;
+ end if;
+ end if;
+ end process dequeue;
+
+end architecture rtl;
diff --git a/hdl/hdl-prj.json b/hdl/hdl-prj.json
new file mode 100644
index 0000000..b557c2b
--- /dev/null
+++ b/hdl/hdl-prj.json
@@ -0,0 +1,24 @@
+{
+ "options": {
+ "ghdl_analysis": [
+ "-fexplicit",
+ "--std=08"
+ ]
+ },
+ "files": [
+ { "file": "common.vhd", "language": "vhdl" },
+ { "file": "decoder.vhd", "language": "vhdl" },
+ { "file": "hexfile_reader.vhd", "language": "vhdl" },
+ { "file": "fifo.vhd", "language": "vhdl" },
+ { "file": "uart.vhd", "language": "vhdl" },
+ { "file": "ALU.vhd", "language": "vhdl" },
+ { "file": "stack.vhd", "language": "vhdl" },
+ { "file": "dpram.vhd", "language": "vhdl" },
+ { "file": "state_machine.vhd", "language": "vhdl" },
+ { "file": "PIC.vhd", "language": "vhdl" },
+ { "file": "prog_rom.vhd", "language": "vhdl" },
+ { "file": "pic_top.vhd", "language": "vhdl" },
+ { "file": "tb_stack.vhd", "language": "vhdl" },
+ { "file": "tb_pic.vhd", "language": "vhdl" }
+ ]
+}
diff --git a/hdl/hexfile_reader.vhd b/hdl/hexfile_reader.vhd
new file mode 100644
index 0000000..143a264
--- /dev/null
+++ b/hdl/hexfile_reader.vhd
@@ -0,0 +1,342 @@
+-------------------------------------------------------------------------------
+--Usage of the reader:
+--
+--CONSTANT ihex_data : STRING :=
+--"/home/pro/autosub/erkka/digital/matlab/esim.HEX;
+--VARIABLE memory : program_array := (OTHERS => (OTHERS => '0'));
+--
+--read_ihex_file(ihex_data, memory);
+--
+-- If some constant definitions are missing, try to figure them out.
+-- If you can't contact Erkka Laulainen, elaulain@ecdl.tkk.fi, room I313A
+-------------------------------------------------------------------------------
+
+LIBRARY ieee;
+USE ieee.std_logic_1164.ALL;
+--USE ieee.std_logic_arith.ALL;
+USE ieee.numeric_std.ALL;
+USE std.textio.ALL;
+USE ieee.std_logic_textio.ALL;
+USE ieee.std_logic_misc.ALL;
+
+
+PACKAGE read_intel_hex_pack IS
+ CONSTANT debug : BOOLEAN := true;
+
+ ------------------------------------------------------------------------------
+ ----- Design Parameters -----------------------------------------------------
+ ------------------------------------------------------------------------------
+
+ CONSTANT Inst_bits : INTEGER := 14;
+ CONSTANT data_bits : INTEGER := 8;
+ CONSTANT inst_mem_size : INTEGER := 1024;
+ TYPE program_array IS ARRAY (0 TO inst_mem_size-1) OF STD_LOGIC_VECTOR(Inst_bits-1 DOWNTO 0);
+
+ ------------------------------------------------------------------------------
+ ----- Reset Values -----------------------------------------------------------
+ ------------------------------------------------------------------------------
+
+-- CONSTANT W_RESET : STD_ULOGIC_VECTOR(data_bits-1 DOWNTO 0) := "XXXXXXXX";
+-- CONSTANT INDF_RESET : STD_ULOGIC_VECTOR(data_bits-1 DOWNTO 0) := "--------";
+-- CONSTANT TMR0_RESET : STD_ULOGIC_VECTOR(data_bits-1 DOWNTO 0) := "XXXXXXXX";
+-- CONSTANT PCL_RESET : STD_ULOGIC_VECTOR(data_bits-1 DOWNTO 0) := "00000000";
+-- CONSTANT STATUS_RESET : STD_ULOGIC_VECTOR(data_bits-1 DOWNTO 0) := "00011XXX";
+-- CONSTANT FSR_RESET : STD_ULOGIC_VECTOR(data_bits-1 DOWNTO 0) := "XXXXXXXX";
+-- CONSTANT PORTA_RESET : STD_ULOGIC_VECTOR(4 DOWNTO 0) := "XXXXX";
+-- CONSTANT PORTB_RESET : STD_ULOGIC_VECTOR(data_bits-1 DOWNTO 0) := "XXXXXXXX";
+-- CONSTANT EEDATA_RESET : STD_ULOGIC_VECTOR(data_bits-1 DOWNTO 0) := "XXXXXXXX";
+-- CONSTANT EEADR_RESET : STD_ULOGIC_VECTOR(data_bits-1 DOWNTO 0) := "XXXXXXXX";
+-- CONSTANT PCLATH_RESET : STD_ULOGIC_VECTOR(4 DOWNTO 0) := "00000";
+-- CONSTANT INTCON_RESET : STD_ULOGIC_VECTOR(data_bits-1 DOWNTO 0) := "0000000X";
+-- CONSTANT OPTION_REG_RESET : STD_ULOGIC_VECTOR(data_bits-1 DOWNTO 0) := "11111111";
+-- CONSTANT TRISA_RESET : STD_ULOGIC_VECTOR(4 DOWNTO 0) := "11111";
+-- CONSTANT TRISB_RESET : STD_ULOGIC_VECTOR(data_bits-1 DOWNTO 0) := "11111111";
+-- CONSTANT EECON1_RESET : STD_ULOGIC_VECTOR(4 DOWNTO 0) := "0X000";
+-- CONSTANT EECON2_RESET : STD_ULOGIC_VECTOR(data_bits-1 DOWNTO 0) := "--------";
+
+ ------------------------------------------------------------------------------
+ ----- Operations Codes -------------------------------------------------------
+ ------------------------------------------------------------------------------
+
+ ----- BYTE-ORIENTED FILE REGISTER OPERATIONS ---------------------------------
+
+-- CONSTANT ADDWF : STD_ULOGIC_VECTOR := "000111--------";
+-- CONSTANT ANDWF : STD_ULOGIC_VECTOR := "000101--------";
+-- CONSTANT CLRF : STD_ULOGIC_VECTOR := "0000011-------";
+-- CONSTANT CLRW : STD_ULOGIC_VECTOR := "0000010-------";
+-- CONSTANT COMF : STD_ULOGIC_VECTOR := "001001--------";
+-- CONSTANT DECF : STD_ULOGIC_VECTOR := "000011--------";
+-- CONSTANT DECFSZ : STD_ULOGIC_VECTOR := "001011--------";
+-- CONSTANT INCF : STD_ULOGIC_VECTOR := "001010--------";
+-- CONSTANT INCFSZ : STD_ULOGIC_VECTOR := "001111--------";
+-- CONSTANT IORWF : STD_ULOGIC_VECTOR := "000100--------";
+-- CONSTANT MOVF : STD_ULOGIC_VECTOR := "001000--------";
+-- CONSTANT MOVWF : STD_ULOGIC_VECTOR := "0000001-------";
+-- CONSTANT NOP : STD_ULOGIC_VECTOR := "0000000--00000";
+-- CONSTANT RLF : STD_ULOGIC_VECTOR := "001101--------";
+-- CONSTANT RRF : STD_ULOGIC_VECTOR := "001100--------";
+-- CONSTANT SUBWF : STD_ULOGIC_VECTOR := "000010--------";
+-- CONSTANT SWAPF : STD_ULOGIC_VECTOR := "001110--------";
+-- CONSTANT XORWF : STD_ULOGIC_VECTOR := "000110--------";
+
+ ----- BIT-ORIENTED FILE REGISTER OPERATIONS ----------------------------------
+
+-- CONSTANT BCF : STD_ULOGIC_VECTOR := "0100----------";
+-- CONSTANT BSF : STD_ULOGIC_VECTOR := "0101----------";
+-- CONSTANT BTFSC : STD_ULOGIC_VECTOR := "0110----------";
+-- CONSTANT BTFSS : STD_ULOGIC_VECTOR := "0111----------";
+
+ ----- LITERAL AND CONTROL OPERATIONS -----------------------------------------
+
+-- CONSTANT ADDLW : STD_ULOGIC_VECTOR := "11111---------";
+-- CONSTANT ANDLW : STD_ULOGIC_VECTOR := "111001--------";
+-- CONSTANT CALL : STD_ULOGIC_VECTOR := "100-----------";
+-- CONSTANT CLRWDT : STD_ULOGIC_VECTOR := "00000001100100";
+-- CONSTANT GOTO : STD_ULOGIC_VECTOR := "101-----------";
+-- CONSTANT IORLW : STD_ULOGIC_VECTOR := "111000--------";
+-- CONSTANT MOVLW : STD_ULOGIC_VECTOR := "1100----------";
+-- CONSTANT RETFIE : STD_ULOGIC_VECTOR := "00000000001001";
+-- CONSTANT RETLW : STD_ULOGIC_VECTOR := "1101----------";
+-- CONSTANT RET : STD_ULOGIC_VECTOR := "00000000001000";
+-- CONSTANT SLEEP : STD_ULOGIC_VECTOR := "00000001100011";
+-- CONSTANT SUBLW : STD_ULOGIC_VECTOR := "11110---------";
+-- CONSTANT XORLW : STD_ULOGIC_VECTOR := "111010--------";
+
+ ------------------------------------------------------------------------------
+ ----- Special Function Register Address --------------------------------------
+ ------------------------------------------------------------------------------
+
+-- CONSTANT INDF_ADR : STD_ULOGIC_VECTOR := "-0000000";
+-- CONSTANT TMR0_ADR : STD_ULOGIC_VECTOR := "00000001";
+-- CONSTANT PCL_ADR : STD_ULOGIC_VECTOR := "-0000010";
+-- CONSTANT STATUS_ADR : STD_ULOGIC_VECTOR := "-0000011";
+-- CONSTANT FSR_ADR : STD_ULOGIC_VECTOR := "-0000100";
+-- CONSTANT PORTA_ADR : STD_ULOGIC_VECTOR := "00000101";
+-- CONSTANT PORTB_ADR : STD_ULOGIC_VECTOR := "00000110";
+-- CONSTANT EEDATA_ADR : STD_ULOGIC_VECTOR := "00001000";
+-- CONSTANT EEARD_ADR : STD_ULOGIC_VECTOR := "00001001";
+-- CONSTANT PCLATH_ADR : STD_ULOGIC_VECTOR := "-0001010";
+-- CONSTANT INTCON_ADR : STD_ULOGIC_VECTOR := "-0001011";
+-- CONSTANT OPTION_ADR : STD_ULOGIC_VECTOR := "10000001";
+-- CONSTANT TRISA_ADR : STD_ULOGIC_VECTOR := "10000101";
+-- CONSTANT TRISB_ADR : STD_ULOGIC_VECTOR := "10000110";
+-- CONSTANT EECON1_ADR : STD_ULOGIC_VECTOR := "10001000";
+-- CONSTANT EECON2_ADR : STD_ULOGIC_VECTOR := "10001001";
+
+ ------------------------------------------------------------------------------
+ ----- STATUS Register Constants ----------------------------------------------
+ ------------------------------------------------------------------------------
+
+-- CONSTANT RP0_BIT : INTEGER := 5;
+-- CONSTANT TO_BIT : INTEGER := 4;
+-- CONSTANT PD_BIT : INTEGER := 3;
+-- CONSTANT Z_BIT : INTEGER := 2;
+-- CONSTANT DC_BIT : INTEGER := 1;
+-- CONSTANT CARRY_BIT : INTEGER := 0;
+
+ ------------------------------------------------------------------------------
+ ----- INTCON Register Constants ----------------------------------------------
+ ------------------------------------------------------------------------------
+
+-- CONSTANT GIE_BIT : INTEGER := 7;
+-- CONSTANT EEIE_BIT : INTEGER := 6;
+-- CONSTANT T0IE_BIT : INTEGER := 5;
+-- CONSTANT INTE_BIT : INTEGER := 4;
+-- CONSTANT RBIE_BIT : INTEGER := 3;
+-- CONSTANT T0IF_BIT : INTEGER := 2;
+-- CONSTANT INTF_BIT : INTEGER := 1;
+-- CONSTANT RBIF_BIT : INTEGER := 0;
+
+ ------------------------------------------------------------------------------
+ ----- OPTION Register Constants ----------------------------------------------
+ ------------------------------------------------------------------------------
+
+-- CONSTANT RBPU_BIT : INTEGER := 7;
+-- CONSTANT INTEDG_BIT : INTEGER := 6;
+-- CONSTANT T0CS_BIT : INTEGER := 5;
+-- CONSTANT T0SE_BIT : INTEGER := 4;
+-- CONSTANT PSA_BIT : INTEGER := 3;
+-- CONSTANT PS2_BIT : INTEGER := 2;
+-- CONSTANT PS1_BIT : INTEGER := 1;
+-- CONSTANT PS0_BIT : INTEGER := 0;
+
+ PROCEDURE read_ihex_file (program_name : IN STRING; memory : OUT program_array);
+
+END PACKAGE read_intel_hex_pack;
+
+PACKAGE BODY read_intel_hex_pack IS
+
+ PROCEDURE str_to_hex (str : IN STRING; result : INOUT NATURAL) IS
+ VARIABLE ch : CHARACTER;
+ BEGIN
+ result := 0;
+ FOR i IN 1 TO str'LENGTH LOOP
+ ch := str(i);
+ IF '0' <= ch and ch <= '9' THEN
+ result := result*16 + character'pos(ch) - character'pos('0');
+ ELSIF 'A' <= ch and ch <= 'F' THEN
+ result := result*16 + character'pos(ch) - character'pos('A') + 10;
+ ELSIF 'a' <= ch and ch <= 'f' THEN
+ result := result*16 + character'pos(ch) - character'pos('a') + 10;
+ ELSE
+ -- ASSERT 1; REPORT "FAILURE: str_to_hex: Non-hex character encountered!"; SEVERITY FAILURE;
+ END IF;
+ END LOOP;
+ END str_to_hex;
+
+ PROCEDURE read_line_header (L : INOUT LINE; byte_count : INOUT INTEGER; address : INOUT INTEGER; record_type : INOUT INTEGER) IS
+ VARIABLE byte_count_str : STRING(1 to 2);
+ VARIABLE address_str : STRING(1 to 4);
+ VARIABLE record_type_str : STRING(1 to 2);
+ BEGIN
+ -- Read byte count
+ FOR i IN 1 TO 2 LOOP
+ READ(L, byte_count_str(i));
+ END LOOP;
+ str_to_hex(byte_count_str, byte_count);
+ -- ASSERT debug; REPORT "DEBUG read_line_header: byte count is" & byte_count; SEVERITY NOTE;
+ -- Read address
+ FOR i IN 1 TO 4 LOOP
+ READ(L, address_str(i));
+ END LOOP;
+ str_to_hex(address_str, address);
+ -- ASSERT debug; REPORT "DEBUG read_line_header: address is" & address; SEVERITY NOTE;
+ -- Read record type
+ FOR i IN 1 TO 2 LOOP
+ READ(L, record_type_str(i));
+ END LOOP;
+ str_to_hex(record_type_str, record_type);
+ -- ASSERT debug; REPORT "DEBUG read_line_header: record type is" & recod_type; SEVERITY NOTE;
+ END read_line_header;
+
+
+ PROCEDURE read_instruction (L : INOUT LINE; instruction_hex : INOUT INTEGER) IS
+ VARIABLE instruction_str : STRING(1 TO 4);
+ VARIABLE instruction_str_tmp : STRING(1 TO 4);
+ VARIABLE ch : CHARACTER;
+ BEGIN
+ -- L pointer is pointing to the instruction we want to read
+ -- Thus, no need to update pointer
+
+ -- Read instruction
+ FOR i IN 1 TO 4 LOOP
+ READ(L, instruction_str(i));
+ END LOOP;
+
+ -- Swap 2 lower and 2 higher byte: 1234 -> 3412
+ instruction_str_tmp(1 TO 2) := instruction_str(1 to 2);
+ instruction_str(1 TO 2) := instruction_str(3 to 4);
+ instruction_str(3 TO 4) := instruction_str_tmp(1 TO 2);
+ str_to_hex(instruction_str, instruction_hex);
+ -- ASSERT debug; REPORT "DEBUG read_instruction: instruction " & instruction_no & "is " & instruction_hex; SEVERITY NOTE;
+ END read_instruction;
+
+
+ PROCEDURE read_ihex_file (program_name : IN STRING; memory : OUT program_array) IS
+ FILE program : TEXT open READ_MODE is program_name;
+ VARIABLE L : LINE;
+ VARIABLE byte_count : INTEGER := 0;
+ VARIABLE address : NATURAL := 0;
+ VARIABLE record_type : INTEGER := 0;
+ -- VARIABLE base_address : INTEGER := 0;
+ -- VARIABLE current_address : INTEGER := 0;
+ VARIABLE instruction : INTEGER := 0;
+ VARIABLE ch : CHARACTER;
+ BEGIN
+ WHILE NOT ENDFILE(program) LOOP
+ byte_count := 0;
+ address := 0;
+ record_type := 0;
+
+ READLINE(program, L);
+
+ -- Move line pointer over semicolon
+ READ(L, ch);
+ -- Read first byte count, address and record type on the line
+ read_line_header(L, byte_count, address, record_type);
+
+ CASE record_type IS
+
+ WHEN 0 => -- Data record
+ FOR i IN 1 TO byte_count/2 LOOP -- toimiiko jos byte count == 1?
+ read_instruction(L, instruction);
+ memory(address/2+i-1) := STD_LOGIC_VECTOR(to_unsigned(instruction, Inst_bits)); -- XXX
+ END LOOP;
+
+ WHEN 1 => -- EOF record
+ NULL;
+
+ WHEN 2 => -- Extended Segment Address Record
+ -- ASSERT 1; REPORT "Extended Segment Address record type 0x02 not implemented"; SEVERITY FAILURE;
+
+ WHEN 3 => -- Start Segment Address Record
+ -- ASSERT 1; REPORT "Start Segment Address record type 0x03 not implemented"; SEVERITY FAILURE;
+
+ WHEN 4 => --
+ -- FOR i IN 0 TO 1 LOOP
+ read_instruction(L, instruction);
+ -- ASSERT (instruction /= 0); REPORT "Extended Linear Address Record not zero";SEVERITY FAILURE;
+ -- END LOOP;
+
+ WHEN 5 => -- Start Linear Address Record
+ -- ASSERT 1; REPORT "Start Linear Address record type 0x05 not implemented"; SEVERITY FAILURE;
+
+ WHEN OTHERS =>
+ -- ASSERT 1; REPORT "Invalid Intel HEX format record type"; SEVERITY FAILURE;
+
+ END CASE;
+
+ END LOOP; -- Read file
+ END PROCEDURE read_ihex_file;
+
+END PACKAGE BODY read_intel_hex_pack;
+
+--PROCEDURE read_ihex_file (program_name : IN STRING; memory : OUT program_array) IS -- pitaako maaritella tyyppi muualla??
+-- FILE program : TEXT IS IN "/home/pro/autosub/erkka/digital/modelsim/" & program_name; -- saako program namen?
+-- TYPE program_array IS ARRAY (0 TO inst_mem_size-1) OF STD_LOGIC_VECTOR(Inst_bits-1 DOWNTO 0);
+-- VARIABLE L : LINE;
+-- VARIABLE byte_count : INTEGER := 0;
+-- VARIABLE address : INTEGER := 0;
+-- VARIABLE record_type : INTEGER := 0;
+-- VARIABLE base_address : INTEGER := 0;
+-- -- VARIABLE current_address : INTEGER := 0;
+-- VARIABLE instruction : INTEGER := 0;
+--BEGIN
+-- WHILE NOT ENDFILE(program) LOOP
+-- READLINE(program, L);
+-- read_line_header(L, byte_count, address, record_type);
+--
+-- CASE record_type IS
+--
+-- WHEN X"00" => -- Data record
+-- FOR i IN 0 TO byte_count-1 LOOP -- toimiiko jos byte count == 1?
+-- read_instruction(L, i, instruction);
+-- program_array(address/2 + i) := instruction; -- tsekkaa
+-- END LOOP;
+--
+-- WHEN X"01" => -- EOF record
+-- NULL;
+--
+-- WHEN X"02" => -- Extended Segment Address Record
+-- ASSERT 1; REPORT "Extended Segment Address record type 0x02 not implemented"; SEVERITY FAILURE;
+--
+-- WHEN X"03" => -- Start Segment Address Record
+-- ASSERT 1; REPORT "Start Segment Address record type 0x03 not implemented"; SEVERITY FAILURE;
+--
+-- WHEN X"04" => --
+-- FOR i IN 0 TO 1 LOOP
+-- read_instruction(L, i, instruction);
+-- ASSERT (instruction /= 0); REPORT "Extended Linear Address Record not zero";SEVERITY FAILURE;
+-- END LOOP;
+--
+-- WHEN X"05" => -- Start Linear Address Record
+-- ASSERT 1; REPORT "Start Linear Address record type 0x05 not implemented"; SEVERITY FAILURE;
+--
+-- WHEN OTHERS =>
+-- ASSERT 1; REPORT "Invalid Intel HEX format record type"; SEVERITY FAILURE;
+--
+-- END CASE;
+--
+-- END LOOP; -- Read file
+--END PROCESS read_ihex_file;
+--
diff --git a/hdl/pic_top.vhd b/hdl/pic_top.vhd
new file mode 100644
index 0000000..f0cb4bd
--- /dev/null
+++ b/hdl/pic_top.vhd
@@ -0,0 +1,81 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+-- FPGA top-level for the Tang Primer 25K (GW5A-LV25MG121).
+-- Pin assignments live in primer25k.cst.
+-- The prog_rom entity holds the compiled program; swap its 'rom' constant
+-- to deploy a new program without changing anything else.
+
+entity pic_top is
+ port (
+ clk : in std_logic; -- 50 MHz board clock (E2)
+ reset_btn : in std_logic; -- H11 user button, active high, pulled down
+ uart_rx : in std_logic; -- B3
+ uart_tx : out std_logic; -- C3
+ activity_led : out std_logic; -- E8 — driven by port_b(0)
+ led : out std_logic -- L6 — driven by port_a(0), blink from C
+ );
+end entity pic_top;
+
+architecture rtl of pic_top is
+
+ constant CLK_FREQ : positive := 50_000_000;
+
+ -- Power-on reset: hold reset for 64 cycles then release
+ signal rst_cnt : unsigned(5 downto 0) := (others => '0');
+ signal reset : std_logic := '1';
+
+ signal pc : std_logic_vector(12 downto 0);
+ signal opcode : std_logic_vector(13 downto 0);
+ signal port_a : std_logic_vector(7 downto 0);
+ signal port_b : std_logic_vector(7 downto 0);
+ signal work_reg : std_logic_vector(7 downto 0);
+ signal status : std_logic_vector(2 downto 0);
+ signal instr_ret : std_logic;
+
+begin
+
+ por : process(clk)
+ begin
+ if rising_edge(clk) then
+ if reset_btn = '1' then
+ rst_cnt <= (others => '0'); -- re-arm counter while button held
+ reset <= '1';
+ elsif rst_cnt(5) = '0' then
+ rst_cnt <= rst_cnt + 1;
+ reset <= '1';
+ else
+ reset <= '0';
+ end if;
+ end if;
+ end process por;
+
+ activity_led <= port_b(0);
+ led <= port_a(0);
+
+ rom : entity work.prog_rom
+ port map (
+ addr => pc,
+ data => opcode
+ );
+
+ cpu : entity work.PIC
+ generic map (
+ CLK_FREQ => CLK_FREQ
+ )
+ port map (
+ clk => clk,
+ reset => reset,
+ opcode => opcode,
+ program_counter => pc,
+ instruction_return => instr_ret,
+ work_reg => work_reg,
+ status => status,
+ port_a => port_a,
+ port_b => port_b,
+ uart_tx => uart_tx,
+ uart_rx => uart_rx
+ );
+
+end architecture rtl;
diff --git a/hdl/stack.vhd b/hdl/stack.vhd
new file mode 100644
index 0000000..fd376c0
--- /dev/null
+++ b/hdl/stack.vhd
@@ -0,0 +1,51 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+entity stack is
+ generic (
+ DEPTH : positive := 8;
+ WIDTH : positive := 13
+ );
+ port (
+ clk : in std_logic;
+ reset : in std_logic;
+ push : in std_logic;
+ pop : in std_logic;
+ din : in std_logic_vector(WIDTH - 1 downto 0);
+ dout : out std_logic_vector(WIDTH - 1 downto 0)
+ );
+end entity stack;
+
+architecture rtl of stack is
+
+ type stack_t is
+ array (0 to DEPTH - 1)
+ of std_logic_vector(WIDTH - 1 downto 0);
+
+ signal mem : stack_t
+ := (others => (others => '0'));
+
+ -- write pointer
+ signal top : unsigned(2 downto 0)
+ := (others => '0');
+begin
+
+ dout <=
+ mem(to_integer(top - 1)) when top /= 0 else (others => '0');
+
+ stck : process(clk, reset)
+ begin
+ if reset = '1' then
+ top <= (others => '0');
+ elsif rising_edge(clk) then
+ if push = '1' then
+ mem(to_integer(top)) <= din;
+ top <= top + 1;
+ elsif pop = '1' then
+ top <= top - 1;
+ end if;
+ end if;
+ end process;
+
+end architecture rtl;
diff --git a/hdl/state_machine.vhd b/hdl/state_machine.vhd
new file mode 100644
index 0000000..5b1c623
--- /dev/null
+++ b/hdl/state_machine.vhd
@@ -0,0 +1,230 @@
+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';
+ -- branch instructions complete over 2 cycles; pulse instr_ret only
+ -- on the phantom NOP cycle (below), not here
+ if instr.op /= GOTO and instr.op /= CALL and
+ instr.op /= RETUR and instr.op /= RETLW then
+ instr_ret <= '1';
+ end if;
+ elsif not is_skip_phantom then
+ -- branch phantom NOP: this is the true end of the 2-cycle instruction
+ instr_ret <= '1';
+ end if;
+ state <= IFetch;
+
+ end case;
+ end if; -- reset
+ end if;
+ end process fsm;
+
+end architecture rtl;
diff --git a/hdl/tb_pic.vhd b/hdl/tb_pic.vhd
new file mode 100644
index 0000000..6e82ae2
--- /dev/null
+++ b/hdl/tb_pic.vhd
@@ -0,0 +1,83 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+use work.read_intel_hex_pack.all;
+
+entity tb_pic is
+end entity tb_pic;
+
+architecture behavioural of tb_pic is
+ constant T_clk : time := 20 ns; -- 50M
+ constant HEX_FILE : string := "build/program.hex";
+ constant SIM_CYCLES : integer := 800_000;
+
+ signal clk : std_logic := '0';
+ signal reset : std_logic := '1';
+ signal opcode : std_logic_vector(13 downto 0);
+
+ signal program_counter : std_logic_vector(12 downto 0);
+ signal instruction_return : std_logic;
+ signal work_reg : std_logic_vector(7 downto 0);
+ signal status : std_logic_vector(2 downto 0);
+ signal port_a : std_logic_vector(7 downto 0);
+ signal port_b : std_logic_vector(7 downto 0);
+
+ signal done : boolean := false;
+
+ signal uart_tx : std_logic;
+ signal uart_rx : std_logic := '1'; -- idle high (no incoming bytes)
+
+ -- program memory: 1024 x 14-bit
+ signal prog_mem : program_array := (others => (others => '0'));
+
+begin
+
+ clk <= not clk after T_clk / 2 when not done else unaffected;
+
+ -- drive opcode from program memory using PC
+ opcode <= prog_mem(to_integer(unsigned(program_counter)));
+
+ dut : entity work.PIC(rtl)
+ generic map (
+ CLK_FREQ => 50_000_000 -- 10 MHz, matches T_clk = 100 ns
+ )
+ port map (
+ clk => clk,
+ reset => reset,
+ opcode => opcode,
+ program_counter => program_counter,
+ instruction_return => instruction_return,
+ work_reg => work_reg,
+ status => status,
+ port_a => port_a,
+ port_b => port_b,
+ uart_tx => uart_tx,
+ uart_rx => uart_rx
+ );
+
+ stimulus : process is
+ variable mem : program_array;
+ begin
+ -- load hex file into program memory
+ read_ihex_file(HEX_FILE, mem);
+ prog_mem <= mem;
+
+ -- reset pulse
+ reset <= '1';
+ wait for T_clk * 2;
+ reset <= '0';
+
+ -- run for SIM_CYCLES instruction cycles
+ for i in 0 to SIM_CYCLES - 1 loop
+ wait until rising_edge(clk);
+ end loop;
+
+ report "simulation done. port_a=" & integer'image(to_integer(unsigned(port_a)))
+ & " port_b=" & integer'image(to_integer(unsigned(port_b)))
+ severity note;
+
+ done <= true;
+ wait;
+ end process stimulus;
+
+end architecture behavioural;
diff --git a/hdl/tb_stack.vhd b/hdl/tb_stack.vhd
new file mode 100644
index 0000000..5b386e9
--- /dev/null
+++ b/hdl/tb_stack.vhd
@@ -0,0 +1,121 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+entity tb_stack is
+end entity tb_stack;
+
+architecture behavioural of tb_stack is
+ constant T_clk : time := 10 ns;
+ constant W : positive := 13;
+
+ signal clk : std_logic := '0';
+ signal reset : std_logic := '0';
+ signal push : std_logic := '0';
+ signal pop : std_logic := '0';
+ signal din : std_logic_vector(W - 1 downto 0) := (others => '0');
+ signal dout : std_logic_vector(W - 1 downto 0);
+
+ signal done : boolean := false;
+
+ procedure tick(signal clk_in : in std_logic) is
+ begin
+ wait until rising_edge(clk_in);
+ wait for 1 ns;
+ end procedure;
+
+begin
+
+ clk <= not clk after T_clk / 2 when not done else unaffected;
+
+ dut : entity work.stack(rtl)
+ generic map (DEPTH => 8, WIDTH => W)
+ port map (
+ clk => clk,
+ reset => reset,
+ push => push,
+ pop => pop,
+ din => din,
+ dout => dout
+ );
+
+ stimulus : process is
+ begin
+
+ -- test 1: reset clears stack
+ report "test 1: reset";
+ reset <= '1';
+ wait for T_clk;
+ reset <= '0';
+ wait for 1 ns;
+ assert dout = std_logic_vector(to_unsigned(0, W))
+ report "reset: dout should be 0" severity error;
+
+ -- test 2: push one value and read it back
+ report "test 2: push/pop single value";
+ din <= std_logic_vector(to_unsigned(16#42#, W));
+ push <= '1';
+ tick(clk);
+ push <= '0';
+ assert dout = std_logic_vector(to_unsigned(16#42#, W))
+ report "push: dout should be 0x42" severity error;
+ pop <= '1';
+ tick(clk);
+ pop <= '0';
+ assert dout = std_logic_vector(to_unsigned(0, W))
+ report "pop: dout should be 0 (empty)" severity error;
+
+ -- test 3: push multiple values, pop in LIFO order
+ report "test 3: LIFO order";
+ din <= std_logic_vector(to_unsigned(16#100#, W));
+ push <= '1';
+ tick(clk);
+ din <= std_logic_vector(to_unsigned(16#200#, W));
+ tick(clk);
+ din <= std_logic_vector(to_unsigned(16#300#, W));
+ tick(clk);
+ push <= '0';
+ assert dout = std_logic_vector(to_unsigned(16#300#, W))
+ report "LIFO: top should be 0x300" severity error;
+ pop <= '1';
+ tick(clk);
+ assert dout = std_logic_vector(to_unsigned(16#200#, W))
+ report "LIFO: after 1 pop should be 0x200" severity error;
+ tick(clk);
+ assert dout = std_logic_vector(to_unsigned(16#100#, W))
+ report "LIFO: after 2 pops should be 0x100" severity error;
+ tick(clk);
+ pop <= '0';
+ assert dout = std_logic_vector(to_unsigned(0, W))
+ report "LIFO: stack empty, dout should be 0" severity error;
+
+ report "test 4: overflow wraparound";
+ push <= '1';
+ for i in 1 to 8 loop
+ din <= std_logic_vector(to_unsigned(i, W));
+ tick(clk);
+ end loop;
+ push <= '0';
+ -- after 8 pushes top wraps to 0, so stack looks empty
+ assert dout = std_logic_vector(to_unsigned(0, W))
+ report "overflow: top wrapped to 0, dout should be 0" severity error;
+
+ -- test 5: reset mid-operation clears pointer
+ report "test 5: reset mid-operation";
+ din <= std_logic_vector(to_unsigned(16#1FF#, W));
+ push <= '1';
+ tick(clk);
+ push <= '0';
+ reset <= '1';
+ wait for T_clk;
+ reset <= '0';
+ wait for 1 ns;
+ assert dout = std_logic_vector(to_unsigned(0, W))
+ report "reset mid-op: dout should be 0" severity error;
+
+ report "All stack tests done" severity note;
+ done <= true;
+ wait;
+ end process stimulus;
+
+end architecture behavioural;
diff --git a/hdl/uart.vhd b/hdl/uart.vhd
new file mode 100644
index 0000000..a0fad93
--- /dev/null
+++ b/hdl/uart.vhd
@@ -0,0 +1,195 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+-- TX: fire and forget (8E1)
+-- RX: FIFO-backed (8E1), consumer reads via rd_en/rx_data/rx_empty
+
+entity uart is
+ generic (
+ CLK_FREQ : positive := 50_000_000;
+ BAUD_RATE : positive := 115_200;
+ CLKS_PER_BIT : positive := CLK_FREQ / BAUD_RATE
+ );
+ port (
+ clk : in std_logic;
+ -- TX
+ tx_should_send : in std_logic;
+ byte_in : in std_logic_vector(7 downto 0);
+ tx_done : out std_logic;
+ baud_tick : out std_logic;
+ uart_tx : out std_logic;
+ -- RX
+ uart_rx : in std_logic;
+ rd_en : in std_logic;
+ rx_data : out std_logic_vector(7 downto 0);
+ rx_empty : out std_logic;
+ rx_full : out std_logic
+ );
+end entity uart;
+
+architecture rtl of uart is
+
+ signal baud_counter : std_logic_vector(15 downto 0)
+ := (others => '0');
+ signal baud_tick_i : std_logic := '0';
+
+ signal tx_out : std_logic := '1';
+ signal current_byte : std_logic_vector(7 downto 0) := (others => '0');
+ signal tx_byte_latch : std_logic_vector(7 downto 0) := (others => '0');
+
+ -- RX input synchronizer (2FF, idle state is '1')
+ signal uart_rx_meta : std_logic := '1';
+ signal uart_rx_sync : std_logic := '1';
+
+ -- RX FIFO wiring
+ signal fifo_wr_en : std_logic := '0';
+ signal fifo_din : std_logic_vector(7 downto 0)
+ := (others => '0');
+ signal fifo_full : std_logic := '0';
+
+begin
+
+ uart_tx <= tx_out;
+ baud_tick <= baud_tick_i;
+
+ rx_fifo : entity work.fifo
+ generic map (DEPTH => 64, ADDR_BITS => 6, WIDTH => 8)
+ port map (
+ clk => clk,
+ wr_en => fifo_wr_en,
+ rd_en => rd_en,
+ din => fifo_din,
+ dout => rx_data,
+ full => fifo_full,
+ empty => rx_empty
+ );
+
+ rx_full <= fifo_full;
+
+ uart_rx_synchronizer : process(clk)
+ begin
+ if rising_edge(clk) then
+ uart_rx_meta <= uart_rx;
+ uart_rx_sync <= uart_rx_meta;
+ end if;
+ end process uart_rx_synchronizer;
+
+ uart_baud_timer : process(clk)
+ begin
+ if rising_edge(clk) then
+ if unsigned(baud_counter) = CLKS_PER_BIT - 1 then
+ baud_counter <= (others => '0');
+ baud_tick_i <= '1';
+ else
+ baud_counter <= std_logic_vector(unsigned(baud_counter) + 1);
+ baud_tick_i <= '0';
+ end if;
+ end if;
+ end process uart_baud_timer;
+
+ uart_transmit_byte : process(clk)
+ type t_state is (idle, start, data, parity, stop);
+ variable state : t_state := idle;
+ variable bit_index : unsigned(2 downto 0);
+ variable parity_value : std_logic;
+ begin
+ if rising_edge(clk) then
+ tx_done <= '0';
+ if baud_tick_i = '1' then
+ case state is
+ when idle =>
+ if tx_should_send = '1' then
+ tx_out <= '0'; -- start bit immediately
+ tx_byte_latch <= byte_in; -- latch byte now
+ parity_value := '0';
+ bit_index := (others => '0');
+ state := data;
+ end if;
+ when start =>
+ null; -- unused, kept for completeness
+ when data =>
+ tx_out <= tx_byte_latch(to_integer(bit_index));
+ parity_value := parity_value xor tx_byte_latch(to_integer(bit_index));
+ if bit_index = 7 then
+ state := parity;
+ else
+ state := data;
+ end if;
+ bit_index := bit_index + 1;
+ when parity =>
+ tx_out <= parity_value;
+ state := stop;
+ when stop =>
+ tx_out <= '1';
+ tx_done <= '1';
+ state := idle;
+ end case;
+ end if;
+ end if;
+ end process uart_transmit_byte;
+
+ uart_receive_byte : process(clk)
+ type rx_state_t is (idle, start, data, parity, stop);
+ variable state : rx_state_t := idle;
+ variable bit_index : unsigned(2 downto 0);
+ variable rx_byte : std_logic_vector(7 downto 0);
+ variable parity_val : std_logic;
+ variable rx_counter : unsigned(15 downto 0);
+ begin
+ if rising_edge(clk) then
+ fifo_wr_en <= '0';
+ case state is
+ when idle =>
+ if uart_rx_sync = '0' then
+ rx_counter := to_unsigned(CLKS_PER_BIT / 2, 16);
+ state := start;
+ end if;
+ when start =>
+ if rx_counter = 0 then
+ if uart_rx_sync = '0' then
+ rx_counter := to_unsigned(CLKS_PER_BIT - 1, 16);
+ bit_index := (others => '0');
+ parity_val := '0';
+ state := data;
+ else
+ state := idle;
+ end if;
+ else
+ rx_counter := rx_counter - 1;
+ end if;
+ when data =>
+ if rx_counter = 0 then
+ rx_byte(to_integer(bit_index)) := uart_rx_sync;
+ parity_val := parity_val xor uart_rx_sync;
+ rx_counter := to_unsigned(CLKS_PER_BIT - 1, 16);
+ if bit_index = 7 then
+ state := parity;
+ else
+ bit_index := bit_index + 1;
+ end if;
+ else
+ rx_counter := rx_counter - 1;
+ end if;
+ when parity =>
+ if rx_counter = 0 then
+ rx_counter := to_unsigned(CLKS_PER_BIT - 1, 16);
+ state := stop;
+ else
+ rx_counter := rx_counter - 1;
+ end if;
+ when stop =>
+ if rx_counter = 0 then
+ if uart_rx_sync = '1' and fifo_full = '0' then
+ fifo_din <= rx_byte;
+ fifo_wr_en <= '1';
+ end if;
+ state := idle;
+ else
+ rx_counter := rx_counter - 1;
+ end if;
+ end case;
+ end if;
+ end process uart_receive_byte;
+
+end architecture rtl;