175 lines
5.9 KiB
VHDL
175 lines
5.9 KiB
VHDL
-- -------------------------------------------------------------------------- --
|
|
-- uart_wb.vhd: Basic UART (Wishbone IF)
|
|
--
|
|
-- Copyright (C) 2017 Markus Koch <markus@notsyncing.net>
|
|
--
|
|
-- This Source Code Form is subject to the terms of the Mozilla Public
|
|
-- License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
-- file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
-- -------------------------------------------------------------------------- --
|
|
|
|
-- TODO: WARN: SEL bits ignored.
|
|
-- TODO: Multiple ports is broken (address mapping * 4), plus some ACK error maybe?
|
|
|
|
library ieee;
|
|
use ieee.std_logic_1164.all;
|
|
use ieee.numeric_std.all;
|
|
use ieee.std_logic_misc.OR_REDUCE;
|
|
|
|
library ip;
|
|
use ip.wishbone_package.all;
|
|
|
|
entity uart_wb is
|
|
generic(
|
|
portcount : integer := 1
|
|
);
|
|
port(
|
|
clk : in std_logic;
|
|
rst : in std_logic;
|
|
-- Wishbone
|
|
slave_i : in wishbone_v3_slave_in;
|
|
slave_o : out wishbone_v3_slave_out;
|
|
irq_o : out std_logic_vector(portcount - 1 downto 0);
|
|
-- UART
|
|
rx : in std_logic_vector(portcount - 1 downto 0);
|
|
tx : out std_logic_vector(portcount - 1 downto 0)
|
|
);
|
|
end entity uart_wb;
|
|
|
|
architecture RTL of uart_wb is
|
|
constant addressQuantum : integer := 4;
|
|
constant registersPerCore : integer := 5;
|
|
|
|
type uart_data is array (portcount - 1 downto 0) of std_logic_vector(7 downto 0);
|
|
type uart_ckDiv is array (portcount - 1 downto 0) of std_logic_vector(15 downto 0);
|
|
type uart_register is array (portcount - 1 downto 0) of std_logic_vector(31 downto 0);
|
|
|
|
signal rx_data : uart_data;
|
|
signal rx_byte_ready : std_logic_vector(portcount - 1 downto 0);
|
|
signal rx_error : std_logic_vector(portcount - 1 downto 0);
|
|
signal ckDiv : uart_ckDiv;
|
|
signal parityEnable : std_logic_vector(portcount - 1 downto 0);
|
|
signal parityOdd : std_logic_vector(portcount - 1 downto 0);
|
|
signal twoStopBits : std_logic_vector(portcount - 1 downto 0);
|
|
signal tx_data : uart_data;
|
|
signal tx_strobe : std_logic_vector(portcount - 1 downto 0);
|
|
signal tx_busy : std_logic_vector(portcount - 1 downto 0);
|
|
|
|
signal CR : uart_register;
|
|
signal SR : uart_register;
|
|
signal IMR : uart_register;
|
|
signal MASKEDSR : uart_register;
|
|
|
|
signal data_in_buffered : uart_data;
|
|
|
|
begin
|
|
generate_label : for i in 0 to portcount - 1 generate
|
|
uart_rx_inst : entity work.uart_rx
|
|
port map(
|
|
clk => clk,
|
|
rst => rst,
|
|
data => rx_data(i),
|
|
byte_ready => rx_byte_ready(i),
|
|
error => rx_error(i),
|
|
ckDiv => ckDiv(i),
|
|
parityEnable => parityEnable(i),
|
|
parityOdd => parityOdd(i),
|
|
twoStopBits => twoStopBits(i),
|
|
rx => rx(i)
|
|
);
|
|
uart_tx_inst : entity work.uart_tx
|
|
port map(
|
|
clk => clk,
|
|
rst => rst,
|
|
data => tx_data(i),
|
|
byte_ready => tx_strobe(i),
|
|
busy => tx_busy(i),
|
|
ckDiv => ckDiv(i),
|
|
parityEnable => parityEnable(i),
|
|
parityOdd => parityOdd(i),
|
|
twoStopBits => twoStopBits(i),
|
|
tx => tx(i)
|
|
);
|
|
end generate generate_label;
|
|
|
|
wb : process(rst, clk) is
|
|
begin
|
|
if rst = '1' then
|
|
slave_o.DAT <= (others => '0');
|
|
|
|
for i in 0 to portcount - 1 loop
|
|
SR(i) <= (others => '0');
|
|
CR(i) <= (others => '0');
|
|
IMR(i) <= (others => '0');
|
|
tx_strobe(i) <= '0';
|
|
tx_data(i) <= (others => '0');
|
|
end loop;
|
|
elsif rising_edge(clk) then
|
|
for i in 0 to portcount - 1 loop
|
|
slave_o.ACK <= '0';
|
|
tx_strobe(i) <= '0';
|
|
|
|
--SR update
|
|
if (rx_error(i) = '1') then
|
|
SR(i)(17) <= '1'; -- Set RXEI
|
|
end if;
|
|
if (rx_byte_ready(i) = '1') then
|
|
SR(i)(18) <= '1'; -- Set RXI
|
|
SR(i)(16) <= '1'; -- Set RXNE (no FIFO yet)
|
|
data_in_buffered(i) <= rx_data(i);
|
|
end if;
|
|
|
|
SR(i)(0) <= tx_busy(i); -- TXBF; no FIFO
|
|
SR(i)(1) <= tx_busy(i); -- TXActive
|
|
|
|
-- WB
|
|
if slave_i.CYC = '1' and slave_i.STB = '1' and slave_o.ACK <= '0' then
|
|
if unsigned(slave_i.ADR) = to_unsigned((registersPerCore * i), slave_i.ADR'length) then -- CRx
|
|
slave_o.DAT <= CR(i);
|
|
if slave_i.we = '1' then
|
|
CR(i) <= slave_i.DAT;
|
|
end if;
|
|
elsif unsigned(slave_i.ADR) = to_unsigned((registersPerCore * i) + 1 * addressQuantum, slave_i.ADR'length) then --SRx
|
|
slave_o.DAT <= SR(i);
|
|
if slave_i.we = '1' then
|
|
SR(i) <= slave_i.DAT and (x"00" & "00000110" & x"0000"); -- mask RO bits
|
|
end if;
|
|
elsif unsigned(slave_i.ADR) = to_unsigned((registersPerCore * i) + 2 * addressQuantum, slave_i.ADR'length) then --IMRx
|
|
slave_o.DAT <= IMR(i);
|
|
if slave_i.we = '1' then
|
|
IMR(i) <= slave_i.DAT;
|
|
end if;
|
|
elsif unsigned(slave_i.ADR) = to_unsigned((registersPerCore * i) + 3 * addressQuantum, slave_i.ADR'length) then --ODRx
|
|
slave_o.DAT <= x"000000" & tx_data(i); --(others => '0');
|
|
if slave_i.we = '1' then
|
|
tx_data(i) <= slave_i.DAT(7 downto 0);
|
|
tx_strobe(i) <= '1';
|
|
end if;
|
|
elsif unsigned(slave_i.ADR) = to_unsigned((registersPerCore * i) + 4 * addressQuantum, slave_i.ADR'length) then --IDRx
|
|
slave_o.DAT <= x"000000" & data_in_buffered(i);
|
|
SR(i)(18) <= '0'; -- RXI Clear interrupt
|
|
SR(i)(16) <= '0'; -- RXNE No FIFO -> buffer is immediately empty again
|
|
end if;
|
|
slave_o.ACK <= '1';
|
|
end if;
|
|
end loop;
|
|
end if;
|
|
end process wb;
|
|
|
|
slave_o.RTY <= '0';
|
|
slave_o.ERR <= '0';
|
|
|
|
applyCR : for i in 0 to portcount - 1 generate
|
|
ckDiv(i) <= CR(i)(31 downto 16);
|
|
parityEnable(i) <= CR(i)(1);
|
|
parityOdd(i) <= CR(i)(0);
|
|
twoStopBits(i) <= CR(i)(2);
|
|
end generate applyCR;
|
|
|
|
masking : for i in 0 to portcount - 1 generate
|
|
MASKEDSR(i) <= SR(i) and IMR(i);
|
|
irq_o(i) <= OR_REDUCE(MASKEDSR(i));
|
|
end generate masking;
|
|
|
|
end architecture RTL;
|