summaryrefslogtreecommitdiffstats
path: root/PIC/uart.vhd
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/uart.vhd
downloadvhdl_pic-8bd8dd6ce07fb8e247f651b3c61c94bb1f00895a.tar.gz
vhdl_pic-8bd8dd6ce07fb8e247f651b3c61c94bb1f00895a.zip
starting files and example programs
Diffstat (limited to 'PIC/uart.vhd')
-rw-r--r--PIC/uart.vhd195
1 files changed, 195 insertions, 0 deletions
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;