mor1kx-bemicrocv/cores/uart/uart_wb.vhd

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;