summaryrefslogtreecommitdiffstats
path: root/PIC
diff options
context:
space:
mode:
authorVäinö Kauppila <vaino@vke.fi>2026-04-19 00:33:08 +0300
committerVäinö Kauppila <vaino@vke.fi>2026-04-19 00:33:08 +0300
commit8bd8dd6ce07fb8e247f651b3c61c94bb1f00895a (patch)
tree87bd95bd073d68efd0ad31a34f0d8926c32ff6db /PIC
downloadvhdl_pic-8bd8dd6ce07fb8e247f651b3c61c94bb1f00895a.tar.gz
vhdl_pic-8bd8dd6ce07fb8e247f651b3c61c94bb1f00895a.zip
starting files and example programs
Diffstat (limited to 'PIC')
-rw-r--r--PIC/ALU.vhd211
-rw-r--r--PIC/PIC.vhd181
-rw-r--r--PIC/blink.c35
-rw-r--r--PIC/common.vhd10
-rw-r--r--PIC/decoder.vhd155
-rw-r--r--PIC/dpram.vhd110
-rw-r--r--PIC/fifo.vhd75
-rw-r--r--PIC/hdl-prj.json24
-rw-r--r--PIC/hexfile_reader.vhd342
-rw-r--r--PIC/pic_top.vhd81
-rw-r--r--PIC/prog_rom.vhd1045
-rw-r--r--PIC/stack.vhd51
-rw-r--r--PIC/state_machine.vhd224
-rw-r--r--PIC/tb_pic.vhd83
-rw-r--r--PIC/tb_stack.vhd121
-rw-r--r--PIC/test_uart.c36
-rw-r--r--PIC/uart.vhd195
17 files changed, 2979 insertions, 0 deletions
diff --git a/PIC/ALU.vhd b/PIC/ALU.vhd
new file mode 100644
index 0000000..ac770a6
--- /dev/null
+++ b/PIC/ALU.vhd
@@ -0,0 +1,211 @@
+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
+ do_skip_out <= '1' when unsigned(t) = 0 else '0';
+ 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 =>
+ 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))));
+
+ 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/PIC/PIC.vhd b/PIC/PIC.vhd
new file mode 100644
index 0000000..a31bec7
--- /dev/null
+++ b/PIC/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/PIC/blink.c b/PIC/blink.c
new file mode 100644
index 0000000..6789b0a
--- /dev/null
+++ b/PIC/blink.c
@@ -0,0 +1,35 @@
+#include <pic16f84a.h>
+
+typedef unsigned char uint8_t;
+
+// Global volatile so sdcc cannot optimise the delay loops away.
+static volatile uint8_t _d0, _d1, _d2;
+
+// Busy-wait ~500 ms at 50 MHz.
+// 200 × 256 × 256 = 13 107 200 inner iterations.
+// Each iteration ≈ 3 uint8_t instructions × 20 ns = 60 ns.
+// Total ≈ 786 ms (tune outer count _d0 < N to taste).
+static void delay(void) {
+ _d0 = 0;
+ do {
+ _d1 = 0;
+ do {
+ _d2 = 0;
+ do { _d2++; } while (_d2 != 0);
+ } while (++_d1 != 30);
+ _d0++;
+ } while (_d0 != 116);
+}
+
+void main(void) {
+ // port_b(0) drives activity_led at E8 on the Tang Primer 25K.
+ // port_a(0) drives led at L6. Both ports are always outputs in this CPU.
+ while (1) {
+ PORTB = 0x01; // activity_led (E8) on
+ PORTA = 0x01; // led (L6) on
+ delay();
+ PORTB = 0x00; // activity_led (E8) off
+ PORTA = 0x00; // led (L6) off
+ delay();
+ }
+}
diff --git a/PIC/common.vhd b/PIC/common.vhd
new file mode 100644
index 0000000..c986ec5
--- /dev/null
+++ b/PIC/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/PIC/decoder.vhd b/PIC/decoder.vhd
new file mode 100644
index 0000000..5a1eb2c
--- /dev/null
+++ b/PIC/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/PIC/dpram.vhd b/PIC/dpram.vhd
new file mode 100644
index 0000000..44d7adf
--- /dev/null
+++ b/PIC/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/PIC/fifo.vhd b/PIC/fifo.vhd
new file mode 100644
index 0000000..9dd8523
--- /dev/null
+++ b/PIC/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/PIC/hdl-prj.json b/PIC/hdl-prj.json
new file mode 100644
index 0000000..b557c2b
--- /dev/null
+++ b/PIC/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/PIC/hexfile_reader.vhd b/PIC/hexfile_reader.vhd
new file mode 100644
index 0000000..143a264
--- /dev/null
+++ b/PIC/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/PIC/pic_top.vhd b/PIC/pic_top.vhd
new file mode 100644
index 0000000..f0cb4bd
--- /dev/null
+++ b/PIC/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/PIC/prog_rom.vhd b/PIC/prog_rom.vhd
new file mode 100644
index 0000000..6ef8001
--- /dev/null
+++ b/PIC/prog_rom.vhd
@@ -0,0 +1,1045 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+entity prog_rom is
+ generic (
+ DEPTH : positive := 1024
+ );
+ port (
+ addr : in std_logic_vector(12 downto 0);
+ data : out std_logic_vector(13 downto 0)
+ );
+end entity prog_rom;
+
+architecture rtl of prog_rom is
+ type rom_t is array (0 to 1024 - 1) of std_logic_vector(13 downto 0);
+ constant rom : rom_t := (
+ 0 => "00000000000000",
+ 1 => "10100000000010",
+ 2 => "11000000011000",
+ 3 => "00000010011010",
+ 4 => "11000000000001",
+ 5 => "00000010011011",
+ 6 => "11000010000000",
+ 7 => "00000000000000",
+ 8 => "00000000000000",
+ 9 => "10000010011110",
+ 10 => "00000000000000",
+ 11 => "00000000000000",
+ 12 => "01001010000011",
+ 13 => "00000000000000",
+ 14 => "00000010011101",
+ 15 => "00100000011011",
+ 16 => "00000010011100",
+ 17 => "11000000011010",
+ 18 => "00000010011110",
+ 19 => "11000000000001",
+ 20 => "00000010011111",
+ 21 => "01001010000011",
+ 22 => "00000000000000",
+ 23 => "00100000011100",
+ 24 => "00000010100000",
+ 25 => "00100000011101",
+ 26 => "00000010100001",
+ 27 => "11000011111111",
+ 28 => "00011110011100",
+ 29 => "01110000000011",
+ 30 => "00001110011101",
+ 31 => "00100000100001",
+ 32 => "00010000100000",
+ 33 => "01100100000011",
+ 34 => "10100010011010",
+ 35 => "11000000000100",
+ 36 => "00011100011110",
+ 37 => "00000010100000",
+ 38 => "00000110100001",
+ 39 => "00110110100001",
+ 40 => "00100000011111",
+ 41 => "00011110100001",
+ 42 => "00100000100000",
+ 43 => "00000010011010",
+ 44 => "00100000100001",
+ 45 => "00000010011011",
+ 46 => "11000010000000",
+ 47 => "00000000000000",
+ 48 => "00000000000000",
+ 49 => "10000010011110",
+ 50 => "00000000000000",
+ 51 => "00000000000000",
+ 52 => "01001010000011",
+ 53 => "00000000000000",
+ 54 => "00000010100011",
+ 55 => "00100000011011",
+ 56 => "00000010100010",
+ 57 => "00100000011110",
+ 58 => "00000010011010",
+ 59 => "00100000011111",
+ 60 => "00000010011011",
+ 61 => "11000010000000",
+ 62 => "00000000000000",
+ 63 => "00000000000000",
+ 64 => "10000010011110",
+ 65 => "00000000000000",
+ 66 => "00000000000000",
+ 67 => "01001010000011",
+ 68 => "00000000000000",
+ 69 => "00000010100101",
+ 70 => "00000010100001",
+ 71 => "00100000011011",
+ 72 => "00000010100000",
+ 73 => "00000010100100",
+ 74 => "11000000000010",
+ 75 => "00011100011110",
+ 76 => "00000010100000",
+ 77 => "00000110100001",
+ 78 => "00110110100001",
+ 79 => "00100000011111",
+ 80 => "00011110100001",
+ 81 => "00100000100000",
+ 82 => "00000010011010",
+ 83 => "00100000100001",
+ 84 => "00000010011011",
+ 85 => "11000010000000",
+ 86 => "00000000000000",
+ 87 => "00000000000000",
+ 88 => "10000010011110",
+ 89 => "00000000000000",
+ 90 => "00000000000000",
+ 91 => "01001010000011",
+ 92 => "00000000000000",
+ 93 => "00000010100001",
+ 94 => "00000010100111",
+ 95 => "00100000011011",
+ 96 => "00000010100110",
+ 97 => "00000010100000",
+ 98 => "01001010000011",
+ 99 => "00000000000000",
+ 100 => "00100000100010",
+ 101 => "00000010100110",
+ 102 => "00100000100011",
+ 103 => "00000010100111",
+ 104 => "11000011111111",
+ 105 => "00011110100010",
+ 106 => "01110000000011",
+ 107 => "00001110100011",
+ 108 => "00100000100111",
+ 109 => "00010000100110",
+ 110 => "01100100000011",
+ 111 => "10100010010011",
+ 112 => "00100000100100",
+ 113 => "00000010011010",
+ 114 => "00100000100101",
+ 115 => "00000010011011",
+ 116 => "11000010000000",
+ 117 => "00000000000000",
+ 118 => "00000000000000",
+ 119 => "10000010111111",
+ 120 => "00000000000000",
+ 121 => "00000000000000",
+ 122 => "01001010000011",
+ 123 => "00000000000000",
+ 124 => "00000010100110",
+ 125 => "00100000100000",
+ 126 => "01001010000011",
+ 127 => "01001100000011",
+ 128 => "00000010000100",
+ 129 => "01001110000011",
+ 130 => "01001010000011",
+ 131 => "00000000000000",
+ 132 => "01100000100001",
+ 133 => "01011110000011",
+ 134 => "00100000100110",
+ 135 => "01001010000011",
+ 136 => "01001100000011",
+ 137 => "00000010000000",
+ 138 => "01001010000011",
+ 139 => "00000000000000",
+ 140 => "00101010100100",
+ 141 => "01100100000011",
+ 142 => "00101010100101",
+ 143 => "00101010100000",
+ 144 => "01100100000011",
+ 145 => "00101010100001",
+ 146 => "10100001100010",
+ 147 => "11000000000110",
+ 148 => "01001010000011",
+ 149 => "00000000000000",
+ 150 => "00011110011110",
+ 151 => "01100000000011",
+ 152 => "00101010011111",
+ 153 => "10100000010101",
+ 154 => "00000000000000",
+ 155 => "00000000000000",
+ 156 => "10100011010010",
+ 157 => "00000000001000",
+ 158 => "11101000000000",
+ 159 => "01100100000011",
+ 160 => "10100010100110",
+ 161 => "11101010000000",
+ 162 => "01100100000011",
+ 163 => "10100010110000",
+ 164 => "00000110011011",
+ 165 => "11010000000000",
+ 166 => "00100000011010",
+ 167 => "00000010000100",
+ 168 => "01001110000011",
+ 169 => "01100000011011",
+ 170 => "01011110000011",
+ 171 => "00100000000000",
+ 172 => "00000010011011",
+ 173 => "00101010000100",
+ 174 => "00100000000000",
+ 175 => "00000000001000",
+ 176 => "00000000000000",
+ 177 => "00000000000000",
+ 178 => "10000011001101",
+ 179 => "00000010011001",
+ 180 => "00111110011010",
+ 181 => "00001110011011",
+ 182 => "00101010011011",
+ 183 => "00000000000000",
+ 184 => "00000000000000",
+ 185 => "10000011001101",
+ 186 => "00000010011000",
+ 187 => "00100000011001",
+ 188 => "00000010011011",
+ 189 => "00100000011000",
+ 190 => "00000000001000",
+ 191 => "11101000000000",
+ 192 => "01100100000011",
+ 193 => "10100011000110",
+ 194 => "11101010000000",
+ 195 => "01100100000011",
+ 196 => "10100011001101",
+ 197 => "11010000000000",
+ 198 => "00100000011010",
+ 199 => "00000010000100",
+ 200 => "01001110000011",
+ 201 => "01100000011011",
+ 202 => "01011110000011",
+ 203 => "00100000000000",
+ 204 => "00000000001000",
+ 205 => "00100000011011",
+ 206 => "00000010001010",
+ 207 => "00100000011010",
+ 208 => "00000010000010",
+ 209 => "00000000001000",
+ 210 => "11000000000000",
+ 211 => "00000010101111",
+ 212 => "11000011111101",
+ 213 => "00000010101110",
+ 214 => "11000010000000",
+ 215 => "00000010110000",
+ 216 => "00100000101110",
+ 217 => "00000010011010",
+ 218 => "00100000101111",
+ 219 => "00000010011011",
+ 220 => "00100000110000",
+ 221 => "00000010101100",
+ 222 => "00100000011011",
+ 223 => "00000010101011",
+ 224 => "00100000011010",
+ 225 => "00000010101010",
+ 226 => "00100000101010",
+ 227 => "00000010011010",
+ 228 => "00100000101011",
+ 229 => "00000010011011",
+ 230 => "00100000101100",
+ 231 => "10000010111111",
+ 232 => "00000010101101",
+ 233 => "00100000101101",
+ 234 => "01100100000011",
+ 235 => "10100011110100",
+ 236 => "00100000101101",
+ 237 => "10000100001111",
+ 238 => "00101010101010",
+ 239 => "01100100000011",
+ 240 => "00101010101011",
+ 241 => "01100100000011",
+ 242 => "00101010101100",
+ 243 => "10100011100010",
+ 244 => "01001010000011",
+ 245 => "00100001110010",
+ 246 => "00000010101110",
+ 247 => "01100010101110",
+ 248 => "10100011110100",
+ 249 => "00100001110001",
+ 250 => "10000100001111",
+ 251 => "10100011110100",
+ 252 => "00000000001000",
+ 253 => "11010001001000",
+ 254 => "11010001100101",
+ 255 => "11010001101100",
+ 256 => "11010001101100",
+ 257 => "11010001101111",
+ 258 => "11010000100000",
+ 259 => "11010001100110",
+ 260 => "11010001110010",
+ 261 => "11010001101111",
+ 262 => "11010001101101",
+ 263 => "11010000100000",
+ 264 => "11010001010000",
+ 265 => "11010001001001",
+ 266 => "11010001000011",
+ 267 => "11010000100001",
+ 268 => "11010000001101",
+ 269 => "11010000001010",
+ 270 => "11010000000000",
+ 271 => "00000010101000",
+ 272 => "01001010000011",
+ 273 => "00100001110010",
+ 274 => "00000010101001",
+ 275 => "01100000101001",
+ 276 => "10100100010000",
+ 277 => "00100000101000",
+ 278 => "00000011110000",
+ 279 => "00000000001000",
+ 280 => "11010000000001",
+ 281 => "11010000000000",
+ 282 => "11010000100000",
+ 283 => "11010000000001",
+ 284 => "11010000110001",
+ 285 => "11010000000000",
+ 286 => "11010000000001",
+ 287 => "11010000000000",
+ 288 => "11010000000000",
+ 289 => "00000000000000",
+ 290 => "00000000000000",
+ 291 => "00000000000000",
+ 292 => "00000000000000",
+ 293 => "00000000000000",
+ 294 => "00000000000000",
+ 295 => "00000000000000",
+ 296 => "00000000000000",
+ 297 => "00000000000000",
+ 298 => "00000000000000",
+ 299 => "00000000000000",
+ 300 => "00000000000000",
+ 301 => "00000000000000",
+ 302 => "00000000000000",
+ 303 => "00000000000000",
+ 304 => "00000000000000",
+ 305 => "00000000000000",
+ 306 => "00000000000000",
+ 307 => "00000000000000",
+ 308 => "00000000000000",
+ 309 => "00000000000000",
+ 310 => "00000000000000",
+ 311 => "00000000000000",
+ 312 => "00000000000000",
+ 313 => "00000000000000",
+ 314 => "00000000000000",
+ 315 => "00000000000000",
+ 316 => "00000000000000",
+ 317 => "00000000000000",
+ 318 => "00000000000000",
+ 319 => "00000000000000",
+ 320 => "00000000000000",
+ 321 => "00000000000000",
+ 322 => "00000000000000",
+ 323 => "00000000000000",
+ 324 => "00000000000000",
+ 325 => "00000000000000",
+ 326 => "00000000000000",
+ 327 => "00000000000000",
+ 328 => "00000000000000",
+ 329 => "00000000000000",
+ 330 => "00000000000000",
+ 331 => "00000000000000",
+ 332 => "00000000000000",
+ 333 => "00000000000000",
+ 334 => "00000000000000",
+ 335 => "00000000000000",
+ 336 => "00000000000000",
+ 337 => "00000000000000",
+ 338 => "00000000000000",
+ 339 => "00000000000000",
+ 340 => "00000000000000",
+ 341 => "00000000000000",
+ 342 => "00000000000000",
+ 343 => "00000000000000",
+ 344 => "00000000000000",
+ 345 => "00000000000000",
+ 346 => "00000000000000",
+ 347 => "00000000000000",
+ 348 => "00000000000000",
+ 349 => "00000000000000",
+ 350 => "00000000000000",
+ 351 => "00000000000000",
+ 352 => "00000000000000",
+ 353 => "00000000000000",
+ 354 => "00000000000000",
+ 355 => "00000000000000",
+ 356 => "00000000000000",
+ 357 => "00000000000000",
+ 358 => "00000000000000",
+ 359 => "00000000000000",
+ 360 => "00000000000000",
+ 361 => "00000000000000",
+ 362 => "00000000000000",
+ 363 => "00000000000000",
+ 364 => "00000000000000",
+ 365 => "00000000000000",
+ 366 => "00000000000000",
+ 367 => "00000000000000",
+ 368 => "00000000000000",
+ 369 => "00000000000000",
+ 370 => "00000000000000",
+ 371 => "00000000000000",
+ 372 => "00000000000000",
+ 373 => "00000000000000",
+ 374 => "00000000000000",
+ 375 => "00000000000000",
+ 376 => "00000000000000",
+ 377 => "00000000000000",
+ 378 => "00000000000000",
+ 379 => "00000000000000",
+ 380 => "00000000000000",
+ 381 => "00000000000000",
+ 382 => "00000000000000",
+ 383 => "00000000000000",
+ 384 => "00000000000000",
+ 385 => "00000000000000",
+ 386 => "00000000000000",
+ 387 => "00000000000000",
+ 388 => "00000000000000",
+ 389 => "00000000000000",
+ 390 => "00000000000000",
+ 391 => "00000000000000",
+ 392 => "00000000000000",
+ 393 => "00000000000000",
+ 394 => "00000000000000",
+ 395 => "00000000000000",
+ 396 => "00000000000000",
+ 397 => "00000000000000",
+ 398 => "00000000000000",
+ 399 => "00000000000000",
+ 400 => "00000000000000",
+ 401 => "00000000000000",
+ 402 => "00000000000000",
+ 403 => "00000000000000",
+ 404 => "00000000000000",
+ 405 => "00000000000000",
+ 406 => "00000000000000",
+ 407 => "00000000000000",
+ 408 => "00000000000000",
+ 409 => "00000000000000",
+ 410 => "00000000000000",
+ 411 => "00000000000000",
+ 412 => "00000000000000",
+ 413 => "00000000000000",
+ 414 => "00000000000000",
+ 415 => "00000000000000",
+ 416 => "00000000000000",
+ 417 => "00000000000000",
+ 418 => "00000000000000",
+ 419 => "00000000000000",
+ 420 => "00000000000000",
+ 421 => "00000000000000",
+ 422 => "00000000000000",
+ 423 => "00000000000000",
+ 424 => "00000000000000",
+ 425 => "00000000000000",
+ 426 => "00000000000000",
+ 427 => "00000000000000",
+ 428 => "00000000000000",
+ 429 => "00000000000000",
+ 430 => "00000000000000",
+ 431 => "00000000000000",
+ 432 => "00000000000000",
+ 433 => "00000000000000",
+ 434 => "00000000000000",
+ 435 => "00000000000000",
+ 436 => "00000000000000",
+ 437 => "00000000000000",
+ 438 => "00000000000000",
+ 439 => "00000000000000",
+ 440 => "00000000000000",
+ 441 => "00000000000000",
+ 442 => "00000000000000",
+ 443 => "00000000000000",
+ 444 => "00000000000000",
+ 445 => "00000000000000",
+ 446 => "00000000000000",
+ 447 => "00000000000000",
+ 448 => "00000000000000",
+ 449 => "00000000000000",
+ 450 => "00000000000000",
+ 451 => "00000000000000",
+ 452 => "00000000000000",
+ 453 => "00000000000000",
+ 454 => "00000000000000",
+ 455 => "00000000000000",
+ 456 => "00000000000000",
+ 457 => "00000000000000",
+ 458 => "00000000000000",
+ 459 => "00000000000000",
+ 460 => "00000000000000",
+ 461 => "00000000000000",
+ 462 => "00000000000000",
+ 463 => "00000000000000",
+ 464 => "00000000000000",
+ 465 => "00000000000000",
+ 466 => "00000000000000",
+ 467 => "00000000000000",
+ 468 => "00000000000000",
+ 469 => "00000000000000",
+ 470 => "00000000000000",
+ 471 => "00000000000000",
+ 472 => "00000000000000",
+ 473 => "00000000000000",
+ 474 => "00000000000000",
+ 475 => "00000000000000",
+ 476 => "00000000000000",
+ 477 => "00000000000000",
+ 478 => "00000000000000",
+ 479 => "00000000000000",
+ 480 => "00000000000000",
+ 481 => "00000000000000",
+ 482 => "00000000000000",
+ 483 => "00000000000000",
+ 484 => "00000000000000",
+ 485 => "00000000000000",
+ 486 => "00000000000000",
+ 487 => "00000000000000",
+ 488 => "00000000000000",
+ 489 => "00000000000000",
+ 490 => "00000000000000",
+ 491 => "00000000000000",
+ 492 => "00000000000000",
+ 493 => "00000000000000",
+ 494 => "00000000000000",
+ 495 => "00000000000000",
+ 496 => "00000000000000",
+ 497 => "00000000000000",
+ 498 => "00000000000000",
+ 499 => "00000000000000",
+ 500 => "00000000000000",
+ 501 => "00000000000000",
+ 502 => "00000000000000",
+ 503 => "00000000000000",
+ 504 => "00000000000000",
+ 505 => "00000000000000",
+ 506 => "00000000000000",
+ 507 => "00000000000000",
+ 508 => "00000000000000",
+ 509 => "00000000000000",
+ 510 => "00000000000000",
+ 511 => "00000000000000",
+ 512 => "00000000000000",
+ 513 => "00000000000000",
+ 514 => "00000000000000",
+ 515 => "00000000000000",
+ 516 => "00000000000000",
+ 517 => "00000000000000",
+ 518 => "00000000000000",
+ 519 => "00000000000000",
+ 520 => "00000000000000",
+ 521 => "00000000000000",
+ 522 => "00000000000000",
+ 523 => "00000000000000",
+ 524 => "00000000000000",
+ 525 => "00000000000000",
+ 526 => "00000000000000",
+ 527 => "00000000000000",
+ 528 => "00000000000000",
+ 529 => "00000000000000",
+ 530 => "00000000000000",
+ 531 => "00000000000000",
+ 532 => "00000000000000",
+ 533 => "00000000000000",
+ 534 => "00000000000000",
+ 535 => "00000000000000",
+ 536 => "00000000000000",
+ 537 => "00000000000000",
+ 538 => "00000000000000",
+ 539 => "00000000000000",
+ 540 => "00000000000000",
+ 541 => "00000000000000",
+ 542 => "00000000000000",
+ 543 => "00000000000000",
+ 544 => "00000000000000",
+ 545 => "00000000000000",
+ 546 => "00000000000000",
+ 547 => "00000000000000",
+ 548 => "00000000000000",
+ 549 => "00000000000000",
+ 550 => "00000000000000",
+ 551 => "00000000000000",
+ 552 => "00000000000000",
+ 553 => "00000000000000",
+ 554 => "00000000000000",
+ 555 => "00000000000000",
+ 556 => "00000000000000",
+ 557 => "00000000000000",
+ 558 => "00000000000000",
+ 559 => "00000000000000",
+ 560 => "00000000000000",
+ 561 => "00000000000000",
+ 562 => "00000000000000",
+ 563 => "00000000000000",
+ 564 => "00000000000000",
+ 565 => "00000000000000",
+ 566 => "00000000000000",
+ 567 => "00000000000000",
+ 568 => "00000000000000",
+ 569 => "00000000000000",
+ 570 => "00000000000000",
+ 571 => "00000000000000",
+ 572 => "00000000000000",
+ 573 => "00000000000000",
+ 574 => "00000000000000",
+ 575 => "00000000000000",
+ 576 => "00000000000000",
+ 577 => "00000000000000",
+ 578 => "00000000000000",
+ 579 => "00000000000000",
+ 580 => "00000000000000",
+ 581 => "00000000000000",
+ 582 => "00000000000000",
+ 583 => "00000000000000",
+ 584 => "00000000000000",
+ 585 => "00000000000000",
+ 586 => "00000000000000",
+ 587 => "00000000000000",
+ 588 => "00000000000000",
+ 589 => "00000000000000",
+ 590 => "00000000000000",
+ 591 => "00000000000000",
+ 592 => "00000000000000",
+ 593 => "00000000000000",
+ 594 => "00000000000000",
+ 595 => "00000000000000",
+ 596 => "00000000000000",
+ 597 => "00000000000000",
+ 598 => "00000000000000",
+ 599 => "00000000000000",
+ 600 => "00000000000000",
+ 601 => "00000000000000",
+ 602 => "00000000000000",
+ 603 => "00000000000000",
+ 604 => "00000000000000",
+ 605 => "00000000000000",
+ 606 => "00000000000000",
+ 607 => "00000000000000",
+ 608 => "00000000000000",
+ 609 => "00000000000000",
+ 610 => "00000000000000",
+ 611 => "00000000000000",
+ 612 => "00000000000000",
+ 613 => "00000000000000",
+ 614 => "00000000000000",
+ 615 => "00000000000000",
+ 616 => "00000000000000",
+ 617 => "00000000000000",
+ 618 => "00000000000000",
+ 619 => "00000000000000",
+ 620 => "00000000000000",
+ 621 => "00000000000000",
+ 622 => "00000000000000",
+ 623 => "00000000000000",
+ 624 => "00000000000000",
+ 625 => "00000000000000",
+ 626 => "00000000000000",
+ 627 => "00000000000000",
+ 628 => "00000000000000",
+ 629 => "00000000000000",
+ 630 => "00000000000000",
+ 631 => "00000000000000",
+ 632 => "00000000000000",
+ 633 => "00000000000000",
+ 634 => "00000000000000",
+ 635 => "00000000000000",
+ 636 => "00000000000000",
+ 637 => "00000000000000",
+ 638 => "00000000000000",
+ 639 => "00000000000000",
+ 640 => "00000000000000",
+ 641 => "00000000000000",
+ 642 => "00000000000000",
+ 643 => "00000000000000",
+ 644 => "00000000000000",
+ 645 => "00000000000000",
+ 646 => "00000000000000",
+ 647 => "00000000000000",
+ 648 => "00000000000000",
+ 649 => "00000000000000",
+ 650 => "00000000000000",
+ 651 => "00000000000000",
+ 652 => "00000000000000",
+ 653 => "00000000000000",
+ 654 => "00000000000000",
+ 655 => "00000000000000",
+ 656 => "00000000000000",
+ 657 => "00000000000000",
+ 658 => "00000000000000",
+ 659 => "00000000000000",
+ 660 => "00000000000000",
+ 661 => "00000000000000",
+ 662 => "00000000000000",
+ 663 => "00000000000000",
+ 664 => "00000000000000",
+ 665 => "00000000000000",
+ 666 => "00000000000000",
+ 667 => "00000000000000",
+ 668 => "00000000000000",
+ 669 => "00000000000000",
+ 670 => "00000000000000",
+ 671 => "00000000000000",
+ 672 => "00000000000000",
+ 673 => "00000000000000",
+ 674 => "00000000000000",
+ 675 => "00000000000000",
+ 676 => "00000000000000",
+ 677 => "00000000000000",
+ 678 => "00000000000000",
+ 679 => "00000000000000",
+ 680 => "00000000000000",
+ 681 => "00000000000000",
+ 682 => "00000000000000",
+ 683 => "00000000000000",
+ 684 => "00000000000000",
+ 685 => "00000000000000",
+ 686 => "00000000000000",
+ 687 => "00000000000000",
+ 688 => "00000000000000",
+ 689 => "00000000000000",
+ 690 => "00000000000000",
+ 691 => "00000000000000",
+ 692 => "00000000000000",
+ 693 => "00000000000000",
+ 694 => "00000000000000",
+ 695 => "00000000000000",
+ 696 => "00000000000000",
+ 697 => "00000000000000",
+ 698 => "00000000000000",
+ 699 => "00000000000000",
+ 700 => "00000000000000",
+ 701 => "00000000000000",
+ 702 => "00000000000000",
+ 703 => "00000000000000",
+ 704 => "00000000000000",
+ 705 => "00000000000000",
+ 706 => "00000000000000",
+ 707 => "00000000000000",
+ 708 => "00000000000000",
+ 709 => "00000000000000",
+ 710 => "00000000000000",
+ 711 => "00000000000000",
+ 712 => "00000000000000",
+ 713 => "00000000000000",
+ 714 => "00000000000000",
+ 715 => "00000000000000",
+ 716 => "00000000000000",
+ 717 => "00000000000000",
+ 718 => "00000000000000",
+ 719 => "00000000000000",
+ 720 => "00000000000000",
+ 721 => "00000000000000",
+ 722 => "00000000000000",
+ 723 => "00000000000000",
+ 724 => "00000000000000",
+ 725 => "00000000000000",
+ 726 => "00000000000000",
+ 727 => "00000000000000",
+ 728 => "00000000000000",
+ 729 => "00000000000000",
+ 730 => "00000000000000",
+ 731 => "00000000000000",
+ 732 => "00000000000000",
+ 733 => "00000000000000",
+ 734 => "00000000000000",
+ 735 => "00000000000000",
+ 736 => "00000000000000",
+ 737 => "00000000000000",
+ 738 => "00000000000000",
+ 739 => "00000000000000",
+ 740 => "00000000000000",
+ 741 => "00000000000000",
+ 742 => "00000000000000",
+ 743 => "00000000000000",
+ 744 => "00000000000000",
+ 745 => "00000000000000",
+ 746 => "00000000000000",
+ 747 => "00000000000000",
+ 748 => "00000000000000",
+ 749 => "00000000000000",
+ 750 => "00000000000000",
+ 751 => "00000000000000",
+ 752 => "00000000000000",
+ 753 => "00000000000000",
+ 754 => "00000000000000",
+ 755 => "00000000000000",
+ 756 => "00000000000000",
+ 757 => "00000000000000",
+ 758 => "00000000000000",
+ 759 => "00000000000000",
+ 760 => "00000000000000",
+ 761 => "00000000000000",
+ 762 => "00000000000000",
+ 763 => "00000000000000",
+ 764 => "00000000000000",
+ 765 => "00000000000000",
+ 766 => "00000000000000",
+ 767 => "00000000000000",
+ 768 => "00000000000000",
+ 769 => "00000000000000",
+ 770 => "00000000000000",
+ 771 => "00000000000000",
+ 772 => "00000000000000",
+ 773 => "00000000000000",
+ 774 => "00000000000000",
+ 775 => "00000000000000",
+ 776 => "00000000000000",
+ 777 => "00000000000000",
+ 778 => "00000000000000",
+ 779 => "00000000000000",
+ 780 => "00000000000000",
+ 781 => "00000000000000",
+ 782 => "00000000000000",
+ 783 => "00000000000000",
+ 784 => "00000000000000",
+ 785 => "00000000000000",
+ 786 => "00000000000000",
+ 787 => "00000000000000",
+ 788 => "00000000000000",
+ 789 => "00000000000000",
+ 790 => "00000000000000",
+ 791 => "00000000000000",
+ 792 => "00000000000000",
+ 793 => "00000000000000",
+ 794 => "00000000000000",
+ 795 => "00000000000000",
+ 796 => "00000000000000",
+ 797 => "00000000000000",
+ 798 => "00000000000000",
+ 799 => "00000000000000",
+ 800 => "00000000000000",
+ 801 => "00000000000000",
+ 802 => "00000000000000",
+ 803 => "00000000000000",
+ 804 => "00000000000000",
+ 805 => "00000000000000",
+ 806 => "00000000000000",
+ 807 => "00000000000000",
+ 808 => "00000000000000",
+ 809 => "00000000000000",
+ 810 => "00000000000000",
+ 811 => "00000000000000",
+ 812 => "00000000000000",
+ 813 => "00000000000000",
+ 814 => "00000000000000",
+ 815 => "00000000000000",
+ 816 => "00000000000000",
+ 817 => "00000000000000",
+ 818 => "00000000000000",
+ 819 => "00000000000000",
+ 820 => "00000000000000",
+ 821 => "00000000000000",
+ 822 => "00000000000000",
+ 823 => "00000000000000",
+ 824 => "00000000000000",
+ 825 => "00000000000000",
+ 826 => "00000000000000",
+ 827 => "00000000000000",
+ 828 => "00000000000000",
+ 829 => "00000000000000",
+ 830 => "00000000000000",
+ 831 => "00000000000000",
+ 832 => "00000000000000",
+ 833 => "00000000000000",
+ 834 => "00000000000000",
+ 835 => "00000000000000",
+ 836 => "00000000000000",
+ 837 => "00000000000000",
+ 838 => "00000000000000",
+ 839 => "00000000000000",
+ 840 => "00000000000000",
+ 841 => "00000000000000",
+ 842 => "00000000000000",
+ 843 => "00000000000000",
+ 844 => "00000000000000",
+ 845 => "00000000000000",
+ 846 => "00000000000000",
+ 847 => "00000000000000",
+ 848 => "00000000000000",
+ 849 => "00000000000000",
+ 850 => "00000000000000",
+ 851 => "00000000000000",
+ 852 => "00000000000000",
+ 853 => "00000000000000",
+ 854 => "00000000000000",
+ 855 => "00000000000000",
+ 856 => "00000000000000",
+ 857 => "00000000000000",
+ 858 => "00000000000000",
+ 859 => "00000000000000",
+ 860 => "00000000000000",
+ 861 => "00000000000000",
+ 862 => "00000000000000",
+ 863 => "00000000000000",
+ 864 => "00000000000000",
+ 865 => "00000000000000",
+ 866 => "00000000000000",
+ 867 => "00000000000000",
+ 868 => "00000000000000",
+ 869 => "00000000000000",
+ 870 => "00000000000000",
+ 871 => "00000000000000",
+ 872 => "00000000000000",
+ 873 => "00000000000000",
+ 874 => "00000000000000",
+ 875 => "00000000000000",
+ 876 => "00000000000000",
+ 877 => "00000000000000",
+ 878 => "00000000000000",
+ 879 => "00000000000000",
+ 880 => "00000000000000",
+ 881 => "00000000000000",
+ 882 => "00000000000000",
+ 883 => "00000000000000",
+ 884 => "00000000000000",
+ 885 => "00000000000000",
+ 886 => "00000000000000",
+ 887 => "00000000000000",
+ 888 => "00000000000000",
+ 889 => "00000000000000",
+ 890 => "00000000000000",
+ 891 => "00000000000000",
+ 892 => "00000000000000",
+ 893 => "00000000000000",
+ 894 => "00000000000000",
+ 895 => "00000000000000",
+ 896 => "00000000000000",
+ 897 => "00000000000000",
+ 898 => "00000000000000",
+ 899 => "00000000000000",
+ 900 => "00000000000000",
+ 901 => "00000000000000",
+ 902 => "00000000000000",
+ 903 => "00000000000000",
+ 904 => "00000000000000",
+ 905 => "00000000000000",
+ 906 => "00000000000000",
+ 907 => "00000000000000",
+ 908 => "00000000000000",
+ 909 => "00000000000000",
+ 910 => "00000000000000",
+ 911 => "00000000000000",
+ 912 => "00000000000000",
+ 913 => "00000000000000",
+ 914 => "00000000000000",
+ 915 => "00000000000000",
+ 916 => "00000000000000",
+ 917 => "00000000000000",
+ 918 => "00000000000000",
+ 919 => "00000000000000",
+ 920 => "00000000000000",
+ 921 => "00000000000000",
+ 922 => "00000000000000",
+ 923 => "00000000000000",
+ 924 => "00000000000000",
+ 925 => "00000000000000",
+ 926 => "00000000000000",
+ 927 => "00000000000000",
+ 928 => "00000000000000",
+ 929 => "00000000000000",
+ 930 => "00000000000000",
+ 931 => "00000000000000",
+ 932 => "00000000000000",
+ 933 => "00000000000000",
+ 934 => "00000000000000",
+ 935 => "00000000000000",
+ 936 => "00000000000000",
+ 937 => "00000000000000",
+ 938 => "00000000000000",
+ 939 => "00000000000000",
+ 940 => "00000000000000",
+ 941 => "00000000000000",
+ 942 => "00000000000000",
+ 943 => "00000000000000",
+ 944 => "00000000000000",
+ 945 => "00000000000000",
+ 946 => "00000000000000",
+ 947 => "00000000000000",
+ 948 => "00000000000000",
+ 949 => "00000000000000",
+ 950 => "00000000000000",
+ 951 => "00000000000000",
+ 952 => "00000000000000",
+ 953 => "00000000000000",
+ 954 => "00000000000000",
+ 955 => "00000000000000",
+ 956 => "00000000000000",
+ 957 => "00000000000000",
+ 958 => "00000000000000",
+ 959 => "00000000000000",
+ 960 => "00000000000000",
+ 961 => "00000000000000",
+ 962 => "00000000000000",
+ 963 => "00000000000000",
+ 964 => "00000000000000",
+ 965 => "00000000000000",
+ 966 => "00000000000000",
+ 967 => "00000000000000",
+ 968 => "00000000000000",
+ 969 => "00000000000000",
+ 970 => "00000000000000",
+ 971 => "00000000000000",
+ 972 => "00000000000000",
+ 973 => "00000000000000",
+ 974 => "00000000000000",
+ 975 => "00000000000000",
+ 976 => "00000000000000",
+ 977 => "00000000000000",
+ 978 => "00000000000000",
+ 979 => "00000000000000",
+ 980 => "00000000000000",
+ 981 => "00000000000000",
+ 982 => "00000000000000",
+ 983 => "00000000000000",
+ 984 => "00000000000000",
+ 985 => "00000000000000",
+ 986 => "00000000000000",
+ 987 => "00000000000000",
+ 988 => "00000000000000",
+ 989 => "00000000000000",
+ 990 => "00000000000000",
+ 991 => "00000000000000",
+ 992 => "00000000000000",
+ 993 => "00000000000000",
+ 994 => "00000000000000",
+ 995 => "00000000000000",
+ 996 => "00000000000000",
+ 997 => "00000000000000",
+ 998 => "00000000000000",
+ 999 => "00000000000000",
+ 1000 => "00000000000000",
+ 1001 => "00000000000000",
+ 1002 => "00000000000000",
+ 1003 => "00000000000000",
+ 1004 => "00000000000000",
+ 1005 => "00000000000000",
+ 1006 => "00000000000000",
+ 1007 => "00000000000000",
+ 1008 => "00000000000000",
+ 1009 => "00000000000000",
+ 1010 => "00000000000000",
+ 1011 => "00000000000000",
+ 1012 => "00000000000000",
+ 1013 => "00000000000000",
+ 1014 => "00000000000000",
+ 1015 => "00000000000000",
+ 1016 => "00000000000000",
+ 1017 => "00000000000000",
+ 1018 => "00000000000000",
+ 1019 => "00000000000000",
+ 1020 => "00000000000000",
+ 1021 => "00000000000000",
+ 1022 => "00000000000000",
+ 1023 => "00000000000000"
+ );
+begin
+ data <= rom(to_integer(unsigned(addr(9 downto 0))));
+end architecture rtl;
diff --git a/PIC/stack.vhd b/PIC/stack.vhd
new file mode 100644
index 0000000..fd376c0
--- /dev/null
+++ b/PIC/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/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;
diff --git a/PIC/tb_pic.vhd b/PIC/tb_pic.vhd
new file mode 100644
index 0000000..067e154
--- /dev/null
+++ b/PIC/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 := "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/PIC/tb_stack.vhd b/PIC/tb_stack.vhd
new file mode 100644
index 0000000..5b386e9
--- /dev/null
+++ b/PIC/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/PIC/test_uart.c b/PIC/test_uart.c
new file mode 100644
index 0000000..f453e21
--- /dev/null
+++ b/PIC/test_uart.c
@@ -0,0 +1,36 @@
+#include <pic16f84a.h>
+
+typedef unsigned char uint8_t;
+
+// UART memory-mapped registers (hooked up in dpram.vhd).
+// Placed at 0x70-0x72: unimplemented in the real PIC16F84A so no
+// conflict with the SFR definitions in pic16f84a.h.
+// 0x70 UART_TX write: transmit a byte
+// 0x71 UART_RX read: receive a byte (pops the RX FIFO)
+// 0x72 UART_STATUS read: bit0=tx_busy, bit1=rx_empty, bit2=rx_full
+__sfr __at(0x70) UART_TX;
+__sfr __at(0x71) UART_RX;
+__sfr __at(0x72) UART_STATUS;
+
+static void uart_send(uint8_t byte) {
+ while (UART_STATUS & 0x01); // wait until tx_busy = 0
+ UART_TX = byte;
+}
+
+static void uart_puts(const char *s) {
+ while (*s) {
+ uart_send((uint8_t)*s);
+ s++;
+ }
+}
+
+void main(void) {
+ uart_puts("Hello from PIC!\r\n");
+
+ // Echo loop: read every incoming byte and send it back
+ while (1) {
+ if (!(UART_STATUS & 0x02)) { // rx_empty = 0 → data available
+ uart_send(UART_RX);
+ }
+ }
+}
diff --git a/PIC/uart.vhd b/PIC/uart.vhd
new file mode 100644
index 0000000..a0fad93
--- /dev/null
+++ b/PIC/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;