summaryrefslogtreecommitdiffstats
path: root/hdl
diff options
context:
space:
mode:
authorVäinö Kauppila <vaino@vke.fi>2026-04-19 19:02:42 +0300
committerVäinö Kauppila <vaino@vke.fi>2026-04-19 19:02:42 +0300
commit46e1210381106707c2df7f07c56023eb6b760763 (patch)
tree6740a17199f76e2965a511dafcbc7ffb61ceafeb /hdl
parenteb8643ddbef43547eb7bfe58789a3908926c7258 (diff)
downloadvhdl_pic-46e1210381106707c2df7f07c56023eb6b760763.tar.gz
vhdl_pic-46e1210381106707c2df7f07c56023eb6b760763.zip
BSRAM progmem. fix Makefile memory command that does not work. add interrupts, bank 1, timer0. Timer0 not tested.
Diffstat (limited to 'hdl')
-rw-r--r--hdl/ALU.vhd9
-rw-r--r--hdl/PIC.vhd93
-rw-r--r--hdl/common.vhd2
-rw-r--r--hdl/decoder.vhd7
-rw-r--r--hdl/dpram.vhd128
-rw-r--r--hdl/hdl-prj.json1
-rw-r--r--hdl/onewire.vhd195
-rw-r--r--hdl/pic_top.vhd7
-rw-r--r--hdl/state_machine.vhd32
-rw-r--r--hdl/timer0.vhd131
10 files changed, 563 insertions, 42 deletions
diff --git a/hdl/ALU.vhd b/hdl/ALU.vhd
index b7c0344..4d3a065 100644
--- a/hdl/ALU.vhd
+++ b/hdl/ALU.vhd
@@ -206,10 +206,11 @@ begin
- when CALL => null;
- when GOTO => null;
- when RETLW => result <= b;
- when RETUR => null;
+ when CALL => null;
+ when GOTO => null;
+ when RETLW => result <= b;
+ when RETUR => null;
+ when RETFIE => null;
end case;
end process;
diff --git a/hdl/PIC.vhd b/hdl/PIC.vhd
index a31bec7..7471add 100644
--- a/hdl/PIC.vhd
+++ b/hdl/PIC.vhd
@@ -22,7 +22,10 @@ entity PIC is
-- UART physical pins
uart_tx : out std_logic;
- uart_rx : in std_logic
+ uart_rx : in std_logic;
+
+ -- 1-Wire physical pin
+ ow_pin : inout std_logic
);
end entity PIC;
@@ -34,12 +37,13 @@ architecture rtl of PIC is
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_qstatus : std_logic_vector(7 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 mem_addr : std_logic_vector(7 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;
@@ -64,6 +68,26 @@ architecture rtl of PIC is
signal uart_rx_empty : std_logic;
signal uart_rx_full : std_logic;
+ -- 1-Wire <-> dpram bridge signals
+ signal ow_tx_byte : std_logic_vector(7 downto 0);
+ signal ow_op : std_logic_vector(1 downto 0);
+ signal ow_start : std_logic;
+ signal ow_rx_byte : std_logic_vector(7 downto 0);
+ signal ow_presence : std_logic;
+ signal ow_busy : std_logic;
+
+ -- Interrupt bridge signals
+ signal irq_pending : std_logic;
+ signal gie_clear : std_logic;
+ signal gie_set : std_logic;
+
+ -- Timer0 bridge signals
+ signal t0if : std_logic;
+ signal tmr0_val : std_logic_vector(7 downto 0);
+ signal tmr0_we_sig : std_logic;
+ signal tmr0_in_sig : std_logic_vector(7 downto 0);
+ signal option_reg : std_logic_vector(7 downto 0);
+
begin
work_reg <= W;
@@ -71,6 +95,9 @@ begin
instruction_return <= instr_ret;
status <= alu_status;
+ -- banking: RP0 (STATUS bit 5) selects bank 0 or bank 1
+ mem_addr <= mem_qstatus(5) & addr;
+
b_operand_select : process(use_literal, sm_data, mem_q)
begin
if use_literal = '1' then
@@ -110,7 +137,10 @@ begin
stack_din => stack_din,
stack_dout => stack_dout,
alu_skip => alu_skip,
- alu_result => alu_result
+ alu_result => alu_result,
+ irq_pending => irq_pending,
+ gie_clear => gie_clear,
+ gie_set => gie_set
);
alu : entity work.alu(rtl)
@@ -119,7 +149,7 @@ begin
b => b_operand,
op => alu_operation,
bit_select => bit_select,
- status_in => mem_qstatus,
+ status_in => mem_qstatus(2 downto 0),
result => alu_result,
status => alu_status,
skip => alu_skip
@@ -136,14 +166,14 @@ begin
);
mem : entity work.dpram(rtl)
- generic map (DEPTH => 128)
+ generic map (DEPTH => 256)
port map (
clk => clk,
we => mem_we,
re => mem_re,
d => alu_result,
d_status => "00000" & alu_status,
- addr => addr,
+ addr => mem_addr,
q => mem_q,
q_status => mem_qstatus,
port_a => port_a,
@@ -155,7 +185,56 @@ begin
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_rx_full => uart_rx_full,
+ ow_tx_byte => ow_tx_byte,
+ ow_op => ow_op,
+ ow_start => ow_start,
+ ow_rx_byte => ow_rx_byte,
+ ow_presence => ow_presence,
+ ow_busy => ow_busy,
+ tmr0_val => tmr0_val,
+ tmr0_we => tmr0_we_sig,
+ tmr0_in => tmr0_in_sig,
+ option_reg => option_reg,
+ t0if_set => t0if,
+ gie_clear => gie_clear,
+ gie_set => gie_set,
+ irq_pending => irq_pending
+ );
+
+ ow_inst : entity work.onewire
+ generic map (
+ CLK_FREQ => CLK_FREQ
+ )
+ port map (
+ clk => clk,
+ reset => reset,
+ ow_pin => ow_pin,
+ start => ow_start,
+ op => ow_op,
+ tx_byte => ow_tx_byte,
+ rx_byte => ow_rx_byte,
+ presence => ow_presence,
+ busy => ow_busy,
+ done => open
+ );
+
+ tmr0_inst : entity work.timer0(rtl)
+ generic map (
+ CLK_FREQ => CLK_FREQ
+ )
+ port map (
+ clk => clk,
+ reset => reset,
+ t0cki => '0',
+ t0cs => option_reg(5),
+ t0se => option_reg(4),
+ psa => option_reg(3),
+ ps => option_reg(2 downto 0),
+ tmr0_we => tmr0_we_sig,
+ tmr0_in => tmr0_in_sig,
+ tmr0_out => tmr0_val,
+ t0if => t0if
);
uart_inst : entity work.uart
diff --git a/hdl/common.vhd b/hdl/common.vhd
index c986ec5..4925f3e 100644
--- a/hdl/common.vhd
+++ b/hdl/common.vhd
@@ -2,7 +2,7 @@ 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,
+ GOTO, MOVLW, RETLW, RETUR, RETFIE, IORWF, NOP,
RLF, RRF, SUBLW, SUBWF, SWAPF, XORLW, XORWF);
-- Status register bits: Z=2, DC=1, C=0
diff --git a/hdl/decoder.vhd b/hdl/decoder.vhd
index 5a1eb2c..07e785d 100644
--- a/hdl/decoder.vhd
+++ b/hdl/decoder.vhd
@@ -63,8 +63,9 @@ package body decoder is
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
+ -- RETURN (0x0008) and RETFIE (0x0009) have bits[13:12]="00"
if opc = "00000000001000" then return LITERAL_OP; end if;
+ if opc = "00000000001001" then return LITERAL_OP; end if;
case opc(13 downto 12) is
-- byte-oriented file-register operations
when "00" =>
@@ -130,8 +131,8 @@ package body decoder is
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;
+ if opc(11 downto 0) = "000000001001" then return RETFIE;
+ elsif 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
diff --git a/hdl/dpram.vhd b/hdl/dpram.vhd
index 44d7adf..92b57f2 100644
--- a/hdl/dpram.vhd
+++ b/hdl/dpram.vhd
@@ -2,23 +2,27 @@ 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):
+-- Data memory with UART and 1-Wire memory-mapped registers.
+-- Addresses 0x70-0x72 are UART (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
+-- Addresses 0x73-0x75 are 1-Wire:
+-- 0x73 OW_DATA write: tx_byte to send; read: rx_byte received
+-- 0x74 OW_CMD write: bits[1:0]=op (00=reset,01=write,10=read), any write pulses start
+-- 0x75 OW_STATUS read: "000000" & presence & busy
entity dpram is
- generic (DEPTH : positive := 128);
+ generic (DEPTH : positive := 256);
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);
+ addr : in std_logic_vector(7 downto 0);
q : out std_logic_vector(7 downto 0);
- q_status : out std_logic_vector(2 downto 0);
+ q_status : out std_logic_vector(7 downto 0);
port_a : out std_logic_vector(7 downto 0);
port_b : out std_logic_vector(7 downto 0);
@@ -31,7 +35,29 @@ entity dpram is
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
+ uart_rx_full : in std_logic;
+
+ -- 1-Wire interface
+ ow_tx_byte : out std_logic_vector(7 downto 0) := (others => '0');
+ ow_op : out std_logic_vector(1 downto 0) := "00";
+ ow_start : out std_logic := '0';
+ ow_rx_byte : in std_logic_vector(7 downto 0);
+ ow_presence : in std_logic;
+ ow_busy : in std_logic;
+
+ -- Timer0 interface
+ tmr0_val : in std_logic_vector(7 downto 0); -- current TMR0 value
+ tmr0_we : out std_logic := '0'; -- CPU writing TMR0
+ tmr0_in : out std_logic_vector(7 downto 0) := (others => '0');
+
+ -- OPTION_REG (bank 1, 0x81) — read by timer0
+ option_reg : out std_logic_vector(7 downto 0);
+
+ -- Interrupt control (INTCON at 0x0B)
+ t0if_set : in std_logic; -- timer0 overflow: sets INTCON.T0IF (bit 2)
+ gie_clear : in std_logic; -- interrupt ack: clears INTCON.GIE (bit 7)
+ gie_set : in std_logic; -- RETFIE: sets INTCON.GIE (bit 7)
+ irq_pending : out std_logic -- GIE & T0IE & T0IF
);
end entity dpram;
@@ -42,10 +68,13 @@ architecture rtl of dpram is
signal mem : mem_t := (others => (others => '0'));
signal uart_tx_pending : std_logic := '0';
+ signal ow_start_i : std_logic := '0';
+ signal ow_op_i : std_logic_vector(1 downto 0) := "00";
+ signal ow_tx_byte_i : std_logic_vector(7 downto 0) := (others => '0');
begin
- -- STATUS output is combinatorial
- q_status <= mem(16#03#)(2 downto 0);
+ -- STATUS output is combinatorial (full 8-bit register)
+ q_status <= mem(16#03#);
port_a <= mem(16#05#);
port_b <= mem(16#06#);
@@ -53,11 +82,21 @@ begin
-- tx_send is high while a transmission is in progress
uart_tx_send <= uart_tx_pending;
+ ow_start <= ow_start_i;
+ ow_op <= ow_op_i;
+ ow_tx_byte <= ow_tx_byte_i;
+
+ option_reg <= mem(16#81#);
+ irq_pending <= mem(16#0B#)(7) and mem(16#0B#)(5) and mem(16#0B#)(2);
+
+ tmr0_we <= '1' when we = '1' and addr = "00000001" else '0';
+ tmr0_in <= d;
+
-- 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
+ if we = '1' and addr = "01110000" then -- 0x70 UART_TX
uart_tx_byte <= d;
uart_tx_pending <= '1';
elsif uart_tx_done = '1' then
@@ -66,18 +105,48 @@ begin
end if;
end process tx_ctrl;
+ ow_ctrl : process(clk)
+ begin
+ if rising_edge(clk) then
+ ow_start_i <= '0';
+ if we = '1' then
+ if addr = "01110011" then -- 0x73 OW_DATA
+ ow_tx_byte_i <= d;
+ elsif addr = "01110100" then -- 0x74 OW_CMD
+ ow_op_i <= d(1 downto 0);
+ ow_start_i <= '1';
+ end if;
+ end if;
+ end if;
+ end process ow_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 addr = "00000000" or
+ addr = "10000000" then -- 0x00/0x80 INDF: indirect via FSR
+ q <= mem(to_integer(unsigned(mem(16#04#))));
+ elsif addr = "00000001" then -- 0x01 TMR0 (bank 0, read from timer0)
+ q <= tmr0_val;
+ elsif addr = "00001011" or
+ addr = "10001011" then -- 0x0B/0x8B INTCON (mirrored)
+ q <= mem(16#0B#);
+ elsif addr = "01110001" then -- 0x71 UART_RX
+ q <= uart_rx_byte;
if uart_rx_empty = '0' then
- uart_rx_rd_en <= '1'; -- advance FIFO pointer
+ uart_rx_rd_en <= '1';
end if;
- elsif addr = "1110010" then -- 0x72 UART_STATUS
+ elsif addr = "01110010" then -- 0x72 UART_STATUS
q <= "00000" & uart_rx_full & uart_rx_empty & uart_tx_pending;
+ elsif addr = "01110011" then -- 0x73 OW_DATA
+ q <= ow_rx_byte;
+ elsif addr = "01110101" then -- 0x75 OW_STATUS
+ q <= "000000" & ow_presence & ow_busy;
+ elsif addr = "00000011" or
+ addr = "10000011" then -- 0x03/0x83 STATUS (mirrored)
+ q <= mem(16#03#);
else
q <= mem(to_integer(unsigned(addr)));
end if;
@@ -89,21 +158,40 @@ begin
begin
if rising_edge(clk) then
if we_status = '1' then
- mem(16#03#) <= d_status;
+ -- only Z/DC/C (bits 2:0) come from ALU; preserve RP0, TO, PD etc.
+ mem(16#03#) <= mem(16#03#)(7 downto 3) & d_status(2 downto 0);
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
+ if addr = "00000000" or
+ addr = "10000000" then -- 0x00/0x80 INDF: indirect via FSR
mem(16#03#) <= d_status;
+ mem(to_integer(unsigned(mem(16#04#)))) <= d;
+ elsif addr = "00000001" then -- 0x01 TMR0: routed to timer0, not RAM
+ mem(16#03#) <= mem(16#03#)(7 downto 3) & d_status(2 downto 0);
+ elsif addr = "00001011" or
+ addr = "10001011" then -- 0x0B/0x8B INTCON (mirrored): direct write
+ mem(16#0B#) <= d;
+ elsif addr = "00000011" or
+ addr = "10000011" then -- 0x03/0x83 STATUS (mirrored): direct write
+ mem(16#03#) <= d;
+ elsif addr = "01110000" or -- 0x70 UART_TX
+ addr = "01110001" or -- 0x71 UART_RX (read-only)
+ addr = "01110010" or -- 0x72 UART_STATUS (read-only)
+ addr = "01110011" or -- 0x73 OW_DATA
+ addr = "01110100" or -- 0x74 OW_CMD
+ addr = "01110101" then -- 0x75 OW_STATUS (read-only)
+ -- peripheral-mapped addresses: don't write to RAM
+ mem(16#03#) <= mem(16#03#)(7 downto 3) & d_status(2 downto 0);
else
mem(16#03#) <= d_status;
mem(to_integer(unsigned(addr))) <= d;
end if;
end if;
+
+ -- Hardware INTCON modifications (last = highest priority)
+ if t0if_set = '1' then mem(16#0B#)(2) <= '1'; end if;
+ if gie_clear = '1' then mem(16#0B#)(7) <= '0'; end if;
+ if gie_set = '1' then mem(16#0B#)(7) <= '1'; end if;
end if;
end process write;
diff --git a/hdl/hdl-prj.json b/hdl/hdl-prj.json
index b557c2b..7a2e576 100644
--- a/hdl/hdl-prj.json
+++ b/hdl/hdl-prj.json
@@ -14,6 +14,7 @@
{ "file": "ALU.vhd", "language": "vhdl" },
{ "file": "stack.vhd", "language": "vhdl" },
{ "file": "dpram.vhd", "language": "vhdl" },
+ { "file": "onewire.vhd", "language": "vhdl" },
{ "file": "state_machine.vhd", "language": "vhdl" },
{ "file": "PIC.vhd", "language": "vhdl" },
{ "file": "prog_rom.vhd", "language": "vhdl" },
diff --git a/hdl/onewire.vhd b/hdl/onewire.vhd
new file mode 100644
index 0000000..440d34d
--- /dev/null
+++ b/hdl/onewire.vhd
@@ -0,0 +1,195 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+entity onewire is
+ generic (
+ CLK_FREQ : positive := 50_000_000
+ );
+ port (
+ clk : in std_logic;
+ reset : in std_logic;
+
+ ow_pin : inout std_logic; -- 4.7 kohm pull-up to 3.3 V externally
+
+ start : in std_logic;
+ op : in std_logic_vector(1 downto 0); -- "00" reset, "01" write, "10" read
+ tx_byte : in std_logic_vector(7 downto 0);
+ rx_byte : out std_logic_vector(7 downto 0);
+ presence : out std_logic;
+ busy : out std_logic;
+ done : out std_logic
+ );
+end entity onewire;
+
+architecture rtl of onewire is
+
+ constant us : positive := CLK_FREQ / 1_000_000;
+
+ constant RESET_LOW_TICKS : positive := 480 * us;
+ constant PRESENCE_WAIT_TICKS : positive := 15 * us;
+ constant PRESENCE_SAMPLE_END : positive := 75 * us;
+ constant RESET_RECOVER_TICKS : positive := 480 * us;
+ constant WRITE0_LOW_TICKS : positive := 60 * us;
+ constant WRITE1_LOW_TICKS : positive := 6 * us;
+ constant SLOT_TICKS : positive := 70 * us;
+ constant READ_INIT_TICKS : positive := 6 * us;
+ constant READ_SAMPLE_TICKS : positive := 15 * us;
+
+ type state_t is (
+ IDLE,
+ RST_PULL, RST_RELEASE, RST_SAMPLE, RST_RECOVER,
+ WR_PULL, WR_RELEASE,
+ RD_PULL, RD_RELEASE, RD_SAMPLE, RD_RECOVER,
+ BYTE_NEXT
+ );
+
+ signal state : state_t := IDLE;
+ signal ow_drive : std_logic := '0';
+ signal ow_sense : std_logic;
+ signal timer : natural range 0 to RESET_LOW_TICKS + 1 := 0;
+ signal bit_index : natural range 0 to 8 := 0;
+ signal shift_reg : std_logic_vector(7 downto 0) := (others => '0');
+ signal cur_bit : std_logic := '0';
+ signal is_read : std_logic := '0';
+ signal rx_byte_i : std_logic_vector(7 downto 0) := (others => '0');
+
+ signal presence_internal : std_logic := '0';
+begin
+
+ ow_pin <= '0' when ow_drive = '1' else 'Z';
+ ow_sense <= ow_pin;
+
+ presence <= presence_internal;
+
+ rx_byte <= rx_byte_i;
+ busy <= '0' when state = IDLE else '1';
+
+ process(clk)
+ begin
+ if rising_edge(clk) then
+ done <= '0';
+ timer <= timer + 1;
+
+ if reset = '1' then
+ state <= IDLE;
+ ow_drive <= '0';
+ timer <= 0;
+ else
+ case state is
+ when IDLE =>
+ timer <= 0;
+
+ if start = '1' then
+ presence_internal <= '0';
+ case op is
+ -- reset
+ when "00" =>
+ timer <= 0;
+ state <= RST_PULL;
+
+ -- write
+ when "01" =>
+ state <= WR_PULL;
+ shift_reg <= tx_byte;
+ bit_index <= 0;
+ is_read <= '0';
+
+ -- read
+ when "10" =>
+ state <= RD_PULL;
+ bit_index <= 0;
+ is_read <= '1';
+
+ when others => null;
+ end case;
+ end if;
+
+ when RST_PULL =>
+ ow_drive <= '1';
+
+ if timer >= RESET_LOW_TICKS then
+ state <= RST_RELEASE;
+ end if;
+
+
+ when RST_RELEASE =>
+ ow_drive <= '0';
+ timer <= 0;
+ state <= RST_SAMPLE;
+
+ when RST_SAMPLE =>
+ if ow_sense = '0' then
+ presence_internal <= '1';
+ end if;
+
+ if (ow_sense = '1' and presence_internal = '1') or
+ timer >= PRESENCE_SAMPLE_END then
+ state <= RST_RECOVER;
+ end if;
+
+ when RST_RECOVER =>
+ -- wait for 480us since RST_RELEASE
+ if timer >= RESET_RECOVER_TICKS then
+ done <= '1';
+ state <= IDLE;
+ end if;
+
+ when WR_PULL =>
+ ow_drive <= '1';
+ if (shift_reg(bit_index) = '1' and timer >= WRITE1_LOW_TICKS) or
+ (shift_reg(bit_index) = '0' and timer >= WRITE0_LOW_TICKS) then
+ state <= WR_RELEASE;
+ end if;
+
+ when WR_RELEASE =>
+ ow_drive <= '0';
+
+ if timer >= SLOT_TICKS then
+ state <= BYTE_NEXT;
+ end if;
+
+ when RD_PULL =>
+ ow_drive <= '1';
+
+ if timer >= READ_INIT_TICKS then
+ state <= RD_RELEASE;
+ end if;
+
+ when RD_RELEASE =>
+ ow_drive <= '0';
+
+ if timer >= READ_INIT_TICKS + 2 * us then
+ state <= RD_SAMPLE;
+ end if;
+
+ when RD_SAMPLE =>
+ rx_byte_i <= ow_sense & rx_byte_i(7 downto 1);
+ state <= RD_RECOVER;
+
+ when RD_RECOVER =>
+ if timer >= SLOT_TICKS then
+ state <= BYTE_NEXT;
+ end if;
+
+ when BYTE_NEXT =>
+ timer <= 0;
+ if bit_index = 7 then
+ bit_index <= 0;
+ done <= '1';
+ state <= IDLE;
+ else
+ bit_index <= bit_index + 1;
+ if is_read = '1' then
+ state <= RD_PULL;
+ else
+ state <= WR_PULL;
+ end if;
+ end if;
+ when others => state <= IDLE;
+ end case;
+ end if;
+ end if;
+ end process;
+
+end architecture rtl;
diff --git a/hdl/pic_top.vhd b/hdl/pic_top.vhd
index f0cb4bd..f5bd386 100644
--- a/hdl/pic_top.vhd
+++ b/hdl/pic_top.vhd
@@ -14,7 +14,8 @@ entity pic_top is
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
+ led : out std_logic; -- L6 — driven by port_a(0), blink from C
+ ow_pin : inout std_logic -- 1-Wire bus (4.7 kohm pull-up externally)
);
end entity pic_top;
@@ -56,6 +57,7 @@ begin
rom : entity work.prog_rom
port map (
+ clk => clk,
addr => pc,
data => opcode
);
@@ -75,7 +77,8 @@ begin
port_a => port_a,
port_b => port_b,
uart_tx => uart_tx,
- uart_rx => uart_rx
+ uart_rx => uart_rx,
+ ow_pin => ow_pin
);
end architecture rtl;
diff --git a/hdl/state_machine.vhd b/hdl/state_machine.vhd
index 5b1c623..1fb5291 100644
--- a/hdl/state_machine.vhd
+++ b/hdl/state_machine.vhd
@@ -31,7 +31,11 @@ entity state_machine is
stack_dout : in std_logic_vector(12 downto 0);
alu_skip : in std_logic;
- alu_result : in std_logic_vector(7 downto 0)
+ alu_result : in std_logic_vector(7 downto 0);
+
+ irq_pending : in std_logic;
+ gie_clear : out std_logic := '0';
+ gie_set : out std_logic := '0'
);
@@ -67,6 +71,8 @@ begin
instr_ret <= '0';
stack_push <= '0';
stack_pop <= '0';
+ gie_clear <= '0';
+ gie_set <= '0';
if reset = '1' then
pc_internal <= (others => '0');
@@ -78,16 +84,30 @@ begin
case state is
when IFetch =>
+ -- Hardware interrupt: push PC, jump to vector, clear GIE
+ if irq_pending = '1' and not do_phantom_nop then
+ stack_din <= pc_internal;
+ stack_push <= '1';
+ pc_internal <= "0000000000100"; -- 0x0004
+ gie_clear <= '1';
+ instr := instruction_decode((others => '0'));
+ is_phantom := true;
+ is_skip_phantom := false;
+ do_phantom_nop := false;
+ state <= MRead;
+
-- if we had a branch before, this cycle is a NOP.
- if do_phantom_nop then
+ elsif 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
+ state <= MRead;
else
instr := instruction_decode(opcode);
is_phantom := false;
is_skip_phantom := false;
+ state <= MRead;
end if;
-- set use_literal based on decoded instruction, holds through MWrite
@@ -106,7 +126,6 @@ begin
-- for bit-select operations
bit_select <= instr.b;
- state <= Mread;
when MRead =>
@@ -145,13 +164,16 @@ begin
do_phantom_nop := true;
is_skip_phantom := false;
- elsif instr.op = RETUR or instr.op = RETLW then
+ elsif instr.op = RETUR or instr.op = RETLW or instr.op = RETFIE then
-- pop the PC from stack.
pc_internal <= stack_dout;
- stack_pop <= '1'; -- pop deferred to next cycle.
+ stack_pop <= '1';
do_phantom_nop := true;
is_skip_phantom := false;
+ if instr.op = RETFIE then
+ gie_set <= '1';
+ end if;
else
pc_internal <= std_logic_vector(unsigned(pc_internal) + 1);
diff --git a/hdl/timer0.vhd b/hdl/timer0.vhd
new file mode 100644
index 0000000..c2846dc
--- /dev/null
+++ b/hdl/timer0.vhd
@@ -0,0 +1,131 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+entity timer0 is
+ generic (
+ CLK_FREQ : positive := 50_000_000
+ );
+ port (
+ clk : in std_logic;
+ reset : in std_logic;
+
+ -- External clock pin (used when T0CS='1')
+ t0cki : in std_logic;
+
+ -- OPTION_REG control bits
+ t0cs : in std_logic; -- 0=Fosc/4, 1=T0CKI
+ t0se : in std_logic; -- 0=rising edge, 1=falling edge
+ psa : in std_logic; -- 0=prescaler->Timer0, 1=prescaler->WDT
+ ps : in std_logic_vector(2 downto 0); -- prescaler rate select
+
+ -- TMR0 register interface (mapped at 0x01)
+ tmr0_we : in std_logic;
+ tmr0_in : in std_logic_vector(7 downto 0);
+ tmr0_out : out std_logic_vector(7 downto 0);
+
+ -- Overflow flag: pulses one clock on FFh->00h rollover
+ t0if : out std_logic
+ );
+end entity timer0;
+
+architecture rtl of timer0 is
+
+ pure function ps_rate(sel : std_logic_vector(2 downto 0)) return natural is
+ begin
+ case sel is
+ when "000" => return 2;
+ when "001" => return 4;
+ when "010" => return 8;
+ when "011" => return 16;
+ when "100" => return 32;
+ when "101" => return 64;
+ when "110" => return 128;
+ when others => return 256;
+ end case;
+ end function;
+
+ -- Fosc/4 tick generator (one pulse every 4 clocks)
+ constant FOSC4_DIV : positive := 4;
+ signal fosc4_cnt : natural range 0 to FOSC4_DIV - 1 := 0;
+ signal fosc4_tick : std_logic := '0';
+
+ -- 2FF synchroniser for the post-mux clock input
+ signal sync_ff1 : std_logic := '0';
+ signal sync_ff2 : std_logic := '0';
+ signal sync_prev : std_logic := '0'; -- for edge detection after sync
+ signal sync_tick : std_logic := '0'; -- one-cycle tick on selected edge
+
+ -- Prescaler
+ signal prescaler : unsigned(7 downto 0) := (others => '0');
+ signal ps_tick : std_logic := '0'; -- one-cycle pulse out of prescaler
+
+ -- TMR0 counter
+ signal tmr0 : unsigned(7 downto 0) := (others => '0');
+
+ -- Write inhibit: per datasheet, 2 cycles after a TMR0 write the
+ -- prescaler increment is blocked to avoid a spurious count
+ signal wr_inhibit : natural range 0 to 2 := 0;
+
+begin
+
+ fosc4_timer_proc : process(clk)
+ begin
+ if rising_edge(clk) then
+
+ fosc4_tick <= '0';
+
+ if fosc4_cnt = FOSC4_DIV - 1 then
+ fosc4_tick <= '1';
+ fosc4_cnt <= 0;
+ else
+ fosc4_cnt <= fosc4_cnt + 1;
+ end if;
+ end if;
+ end process fosc4_timer_proc;
+
+ prescaler_proc : process(clk)
+ begin
+ if rising_edge(clk) then
+ ps_tick <= '0';
+
+ if tmr0_we = '1' then
+ prescaler <= (others => '0');
+ elsif fosc4_tick = '1' and psa = '0' then
+ if to_integer(prescaler) + 1 >= ps_rate(ps) then
+ prescaler <= (others => '0');
+ ps_tick <= '1';
+ else
+ prescaler <= prescaler + 1;
+ end if;
+ end if;
+ end if;
+ end process prescaler_proc;
+
+ tmr0_out <= std_logic_vector(tmr0);
+
+ timer0_proc : process(clk)
+ begin
+ if rising_edge(clk) then
+ t0if <= '0';
+
+ -- 2FF sync
+ sync_ff1 <= '1' when (psa = '1' and fosc4_tick = '1') or
+ (psa = '0' and ps_tick = '1') else '0';
+ sync_ff2 <= sync_ff1;
+
+ if reset = '1' then
+ tmr0 <= (others => '0');
+ sync_ff1 <= '0';
+ sync_ff2 <= '0';
+ elsif tmr0_we = '1' then
+ tmr0 <= unsigned(tmr0_in);
+ elsif sync_ff2 = '1' then
+ if tmr0 = x"FF" then
+ t0if <= '1';
+ end if;
+ tmr0 <= tmr0 + 1;
+ end if;
+ end if;
+ end process timer0_proc;
+end architecture rtl;