forked from cores/microwatt
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
422 lines
15 KiB
VHDL
422 lines
15 KiB
VHDL
5 years ago
|
library ieee;
|
||
|
use ieee.std_logic_1164.all;
|
||
|
use ieee.numeric_std.all;
|
||
|
|
||
|
library work;
|
||
|
use work.sim_console.all;
|
||
|
|
||
|
entity uart_top is
|
||
|
port(
|
||
|
wb_clk_i : in std_ulogic;
|
||
|
wb_rst_i : in std_ulogic;
|
||
|
wb_adr_i : in std_ulogic_vector(2 downto 0);
|
||
|
wb_dat_i : in std_ulogic_vector(7 downto 0);
|
||
|
wb_dat_o : out std_ulogic_vector(7 downto 0);
|
||
|
wb_we_i : in std_ulogic;
|
||
|
wb_stb_i : in std_ulogic;
|
||
|
wb_cyc_i : in std_ulogic;
|
||
|
wb_ack_o : out std_ulogic;
|
||
|
int_o : out std_ulogic;
|
||
|
stx_pad_o : out std_ulogic;
|
||
|
srx_pad_i : in std_ulogic;
|
||
|
rts_pad_o : out std_ulogic;
|
||
|
cts_pad_i : in std_ulogic;
|
||
|
dtr_pad_o : out std_ulogic;
|
||
|
dsr_pad_i : in std_ulogic;
|
||
|
ri_pad_i : in std_ulogic;
|
||
|
dcd_pad_i : in std_ulogic
|
||
|
);
|
||
|
end entity uart_top;
|
||
|
|
||
|
architecture behaviour of uart_top is
|
||
|
|
||
|
-- Call POLL every N clocks to generate interrupts
|
||
|
constant POLL_DELAY : natural := 100;
|
||
|
|
||
|
-- Register definitions
|
||
|
subtype reg_adr_t is std_ulogic_vector(2 downto 0);
|
||
|
|
||
|
constant REG_IDX_RXTX : reg_adr_t := "000";
|
||
|
constant REG_IDX_IER : reg_adr_t := "001";
|
||
|
constant REG_IDX_IIR_FCR : reg_adr_t := "010";
|
||
|
constant REG_IDX_LCR : reg_adr_t := "011";
|
||
|
constant REG_IDX_MCR : reg_adr_t := "100";
|
||
|
constant REG_IDX_LSR : reg_adr_t := "101";
|
||
|
constant REG_IDX_MSR : reg_adr_t := "110";
|
||
|
constant REG_IDX_SCR : reg_adr_t := "111";
|
||
|
|
||
|
-- IER bits
|
||
|
constant REG_IER_RDI_BIT : natural := 0;
|
||
|
constant REG_IER_THRI_BIT : natural := 1;
|
||
|
constant REG_IER_RLSI_BIT : natural := 2;
|
||
|
constant REG_IER_MSI_BIT : natural := 3;
|
||
|
|
||
|
-- IIR bit
|
||
|
constant REG_IIR_NO_INT : natural := 0;
|
||
|
-- IIR values for bit 3 downto 0
|
||
|
constant REG_IIR_RDI : std_ulogic_vector(3 downto 1) := "010";
|
||
|
constant REG_IIR_THRI : std_ulogic_vector(3 downto 1) := "001";
|
||
|
constant REG_IIR_RLSI : std_ulogic_vector(3 downto 1) := "011";
|
||
|
constant REG_IIR_MSI : std_ulogic_vector(3 downto 1) := "000";
|
||
|
|
||
|
-- FCR bits
|
||
|
constant REG_FCR_EN_FIFO_BIT : natural := 0; -- Always 1
|
||
|
constant REG_FCR_CLR_RCVR_BIT : natural := 1;
|
||
|
constant REG_FCR_CLR_XMIT_BIT : natural := 2;
|
||
|
constant REG_FCR_DMA_SEL_BIT : natural := 3; -- Not implemented
|
||
|
-- FCR values for FIFO threshold in bits 7 downto 6
|
||
|
constant REG_FCR_FIFO_TRIG1 : std_ulogic_vector(7 downto 6) := "00";
|
||
|
constant REG_FCR_FIFO_TRIG4 : std_ulogic_vector(7 downto 6) := "01";
|
||
|
constant REG_FCR_FIFO_TRIG8 : std_ulogic_vector(7 downto 6) := "10";
|
||
|
constant REG_FCR_FIFO_TRIG14 : std_ulogic_vector(7 downto 6) := "11";
|
||
|
|
||
|
-- LCR bits
|
||
|
constant REG_LCR_STOP_BIT : natural := 2;
|
||
|
constant REG_LCR_PARITY_BIT : natural := 3;
|
||
|
constant REG_LCR_EPAR_BIT : natural := 4;
|
||
|
constant REG_LCR_SPAR_BIT : natural := 5;
|
||
|
constant REG_LCR_SBC_BIT : natural := 6;
|
||
|
constant REG_LCR_DLAB_BIT : natural := 7;
|
||
|
-- LCR values for data length (bits 1 downto 0)
|
||
|
constant REG_LCR_WLEN5 : std_ulogic_vector(1 downto 0) := "00";
|
||
|
constant REG_LCR_WLEN6 : std_ulogic_vector(1 downto 0) := "01";
|
||
|
constant REG_LCR_WLEN7 : std_ulogic_vector(1 downto 0) := "10";
|
||
|
constant REG_LCR_WLEN8 : std_ulogic_vector(1 downto 0) := "11";
|
||
|
|
||
|
-- MCR bits
|
||
|
constant REG_MCR_DTR_BIT : natural := 0;
|
||
|
constant REG_MCR_RTS_BIT : natural := 1;
|
||
|
constant REG_MCR_OUT1_BIT : natural := 2;
|
||
|
constant REG_MCR_OUT2_BIT : natural := 3;
|
||
|
constant REG_MCR_LOOP_BIT : natural := 4;
|
||
|
|
||
|
-- LSR bits
|
||
|
constant REG_LSR_DR_BIT : natural := 0;
|
||
|
constant REG_LSR_OE_BIT : natural := 1;
|
||
|
constant REG_LSR_PE_BIT : natural := 2;
|
||
|
constant REG_LSR_FE_BIT : natural := 3;
|
||
|
constant REG_LSR_BI_BIT : natural := 4;
|
||
|
constant REG_LSR_THRE_BIT : natural := 5;
|
||
|
constant REG_LSR_TEMT_BIT : natural := 6;
|
||
|
constant REG_LSR_FIFOE_BIT : natural := 7;
|
||
|
|
||
|
-- MSR bits
|
||
|
constant REG_MSR_DCTS_BIT : natural := 0;
|
||
|
constant REG_MSR_DDSR_BIT : natural := 1;
|
||
|
constant REG_MSR_TERI_BIT : natural := 2;
|
||
|
constant REG_MSR_DDCD_BIT : natural := 3;
|
||
|
constant REG_MSR_CTS_BIT : natural := 4;
|
||
|
constant REG_MSR_DSR_BIT : natural := 5;
|
||
|
constant REG_MSR_RI_BIT : natural := 6;
|
||
|
constant REG_MSR_DCD_BIT : natural := 7;
|
||
|
|
||
|
-- Wishbone signals decode:
|
||
|
signal reg_idx : reg_adr_t;
|
||
|
signal wb_phase : std_ulogic;
|
||
|
signal reg_write : std_ulogic;
|
||
|
signal reg_read : std_ulogic;
|
||
|
|
||
|
-- Register storage
|
||
|
signal reg_ier : std_ulogic_vector(3 downto 0);
|
||
|
signal reg_iir : std_ulogic_vector(3 downto 0);
|
||
|
signal reg_fcr : std_ulogic_vector(7 downto 6);
|
||
|
signal reg_lcr : std_ulogic_vector(7 downto 0);
|
||
|
signal reg_mcr : std_ulogic_vector(4 downto 0);
|
||
|
signal reg_lsr : std_ulogic_vector(7 downto 0);
|
||
|
signal reg_msr : std_ulogic_vector(7 downto 0);
|
||
|
signal reg_scr : std_ulogic_vector(7 downto 0);
|
||
|
|
||
|
signal reg_div : std_ulogic_vector(15 downto 0);
|
||
|
|
||
|
-- Control signals
|
||
|
signal rx_fifo_clr : std_ulogic;
|
||
|
signal tx_fifo_clr : std_ulogic;
|
||
|
|
||
|
-- Pending interrupts
|
||
|
signal int_rdi_pending : std_ulogic;
|
||
|
signal int_thri_pending : std_ulogic;
|
||
|
signal int_rlsi_pending : std_ulogic;
|
||
|
signal int_msi_pending : std_ulogic;
|
||
|
|
||
|
-- Actual data output
|
||
|
signal data_out : std_ulogic_vector(7 downto 0) := x"00";
|
||
|
|
||
|
-- Incoming data pending signal
|
||
|
signal data_in_pending : std_ulogic := '0';
|
||
|
|
||
|
-- Useful aliases
|
||
|
alias dlab : std_ulogic is reg_lcr(REG_LCR_DLAB_BIT);
|
||
|
|
||
|
alias clk : std_ulogic is wb_clk_i;
|
||
|
alias rst : std_ulogic is wb_rst_i;
|
||
|
alias cyc : std_ulogic is wb_cyc_i;
|
||
|
alias stb : std_ulogic is wb_stb_i;
|
||
|
alias we : std_ulogic is wb_we_i;
|
||
|
begin
|
||
|
|
||
|
-- Register index shortcut
|
||
|
reg_idx <= wb_adr_i(2 downto 0);
|
||
|
|
||
|
-- 2 phases WB process.
|
||
|
--
|
||
|
-- Among others, this gives us a "free" cycle for the
|
||
|
-- side effects of some accesses percolate in the form
|
||
|
-- of status bit changes in other registers.
|
||
|
wb_cycle: process(clk)
|
||
|
variable phase : std_ulogic := '0';
|
||
|
begin
|
||
|
if rising_edge(clk) then
|
||
|
if wb_phase = '0' then
|
||
|
if cyc = '1' and stb = '1' then
|
||
|
wb_ack_o <= '1';
|
||
|
wb_phase <= '1';
|
||
|
end if;
|
||
|
else
|
||
|
wb_ack_o <= '0';
|
||
|
wb_phase <= '0';
|
||
|
end if;
|
||
|
end if;
|
||
|
end process;
|
||
|
|
||
|
-- Reg read/write signals
|
||
|
reg_write <= cyc and stb and we and not wb_phase;
|
||
|
reg_read <= cyc and stb and not we and not wb_phase;
|
||
|
|
||
|
-- Register read is synchronous to avoid collisions with
|
||
|
-- read-clear side effects
|
||
|
do_reg_read: process(clk)
|
||
|
begin
|
||
|
if rising_edge(clk) then
|
||
|
wb_dat_o <= x"00";
|
||
|
if reg_read = '1' then
|
||
|
case reg_idx is
|
||
|
when REG_IDX_RXTX =>
|
||
|
if dlab = '1' then
|
||
|
wb_dat_o <= reg_div(7 downto 0);
|
||
|
else
|
||
|
wb_dat_o <= data_out;
|
||
|
end if;
|
||
|
when REG_IDX_IER =>
|
||
|
if dlab = '1' then
|
||
|
wb_dat_o <= reg_div(15 downto 8);
|
||
|
else
|
||
|
wb_dat_o <= "0000" & reg_ier;
|
||
|
end if;
|
||
|
when REG_IDX_IIR_FCR =>
|
||
|
-- Top bits always set as FIFO is always enabled
|
||
|
wb_dat_o <= "1100" & reg_iir;
|
||
|
when REG_IDX_LCR =>
|
||
|
wb_dat_o <= reg_lcr;
|
||
|
when REG_IDX_LSR =>
|
||
|
wb_dat_o <= reg_lsr;
|
||
|
when REG_IDX_MSR =>
|
||
|
wb_dat_o <= reg_msr;
|
||
|
when REG_IDX_SCR =>
|
||
|
wb_dat_o <= reg_scr;
|
||
|
when others =>
|
||
|
end case;
|
||
|
end if;
|
||
|
end if;
|
||
|
end process;
|
||
|
|
||
|
-- Receive/send synchronous process
|
||
|
rxtx: process(clk)
|
||
|
variable dp : std_ulogic;
|
||
|
variable poll_cnt : natural;
|
||
|
variable sim_tmp : std_ulogic_vector(63 downto 0);
|
||
|
begin
|
||
|
if rising_edge(clk) then
|
||
|
if rst = '0' then
|
||
|
dp := data_in_pending;
|
||
|
if dlab = '0' and reg_idx = REG_IDX_RXTX then
|
||
|
if reg_write = '1' then
|
||
|
-- FIFO write
|
||
|
-- XXX Simulate the FIFO and delays for more
|
||
|
-- accurate behaviour & interrupts
|
||
|
sim_console_write(x"00000000000000" & wb_dat_i);
|
||
|
end if;
|
||
|
if reg_read = '1' then
|
||
|
dp := '0';
|
||
|
data_out <= x"00";
|
||
|
end if;
|
||
|
end if;
|
||
|
|
||
|
-- Poll for incoming data
|
||
|
if poll_cnt = 0 or (reg_read = '1' and reg_idx = REG_IDX_LSR) then
|
||
|
sim_console_poll(sim_tmp);
|
||
|
poll_cnt := POLL_DELAY;
|
||
|
if dp = '0' and sim_tmp(0) = '1' then
|
||
|
dp := '1';
|
||
|
sim_console_read(sim_tmp);
|
||
|
data_out <= sim_tmp(7 downto 0);
|
||
|
end if;
|
||
|
poll_cnt := poll_cnt - 1;
|
||
|
end if;
|
||
|
data_in_pending <= dp;
|
||
|
end if;
|
||
|
end if;
|
||
|
end process;
|
||
|
|
||
|
-- Interrupt pending bits
|
||
|
int_rdi_pending <= data_in_pending;
|
||
|
int_thri_pending <= '1';
|
||
|
int_rlsi_pending <= reg_lsr(REG_LSR_OE_BIT) or
|
||
|
reg_lsr(REG_LSR_PE_BIT) or
|
||
|
reg_lsr(REG_LSR_FE_BIT) or
|
||
|
reg_lsr(REG_LSR_BI_BIT);
|
||
|
int_msi_pending <= reg_msr(REG_MSR_DCTS_BIT) or
|
||
|
reg_msr(REG_MSR_DDSR_BIT) or
|
||
|
reg_msr(REG_MSR_TERI_BIT) or
|
||
|
reg_msr(REG_MSR_DDCD_BIT);
|
||
|
|
||
|
-- Derive interrupt output from IIR
|
||
|
int_o <= not reg_iir(REG_IIR_NO_INT);
|
||
|
|
||
|
-- Divisor register
|
||
|
div_reg_w: process(clk)
|
||
|
begin
|
||
|
if rising_edge(clk) then
|
||
|
if rst = '1' then
|
||
|
reg_div <= (others => '0');
|
||
|
elsif reg_write = '1' and dlab = '1' then
|
||
|
if reg_idx = REG_IDX_RXTX then
|
||
|
reg_div(7 downto 0) <= wb_dat_i;
|
||
|
elsif reg_idx = REG_IDX_IER then
|
||
|
reg_div(15 downto 8) <= wb_dat_i;
|
||
|
end if;
|
||
|
end if;
|
||
|
end if;
|
||
|
end process;
|
||
|
|
||
|
-- IER register
|
||
|
ier_reg_w: process(clk)
|
||
|
begin
|
||
|
if rising_edge(clk) then
|
||
|
if rst = '1' then
|
||
|
reg_ier <= "0000";
|
||
|
else
|
||
|
if reg_write = '1' and dlab = '0' and reg_idx = REG_IDX_IER then
|
||
|
reg_ier <= wb_dat_i(3 downto 0);
|
||
|
end if;
|
||
|
end if;
|
||
|
end if;
|
||
|
end process;
|
||
|
|
||
|
-- IIR (read only) generation
|
||
|
iir_reg_w: process(clk)
|
||
|
begin
|
||
|
if rising_edge(clk) then
|
||
|
reg_iir <= "0001";
|
||
|
if int_rlsi_pending = '1' and reg_ier(REG_IER_RLSI_BIT) = '1' then
|
||
|
reg_iir <= REG_IIR_RLSI & "0";
|
||
|
elsif int_rdi_pending = '1' and reg_ier(REG_IER_RDI_BIT) = '1' then
|
||
|
reg_iir <= REG_IIR_RDI & "0";
|
||
|
elsif int_thri_pending = '1' and reg_ier(REG_IER_THRI_BIT) = '1' then
|
||
|
reg_iir <= REG_IIR_THRI & "0";
|
||
|
elsif int_msi_pending = '1' and reg_ier(REG_IER_MSI_BIT) = '1' then
|
||
|
reg_iir <= REG_IIR_MSI & "0";
|
||
|
end if;
|
||
|
|
||
|
-- It *seems* like reading IIR should clear THRI for
|
||
|
-- some amount of time until it gets set again a few
|
||
|
-- clocks later if the transmitter is still empty. We
|
||
|
-- don't do that at this point.
|
||
|
end if;
|
||
|
end process;
|
||
|
|
||
|
-- FCR (write only) register
|
||
|
fcr_reg_w: process(clk)
|
||
|
begin
|
||
|
if rising_edge(clk) then
|
||
|
if rst = '1' then
|
||
|
reg_fcr <= "11";
|
||
|
rx_fifo_clr <= '1';
|
||
|
tx_fifo_clr <= '1';
|
||
|
elsif reg_write = '1' and reg_idx = REG_IDX_IIR_FCR then
|
||
|
reg_fcr <= wb_dat_i(7 downto 6);
|
||
|
rx_fifo_clr <= wb_dat_i(REG_FCR_CLR_RCVR_BIT);
|
||
|
tx_fifo_clr <= wb_dat_i(REG_FCR_CLR_XMIT_BIT);
|
||
|
else
|
||
|
rx_fifo_clr <= '0';
|
||
|
tx_fifo_clr <= '0';
|
||
|
end if;
|
||
|
end if;
|
||
|
end process;
|
||
|
|
||
|
-- LCR register
|
||
|
lcr_reg_w: process(clk)
|
||
|
begin
|
||
|
if rising_edge(clk) then
|
||
|
if rst = '1' then
|
||
|
reg_lcr <= "00000011";
|
||
|
elsif reg_write = '1' and reg_idx = REG_IDX_LCR then
|
||
|
reg_lcr <= wb_dat_i;
|
||
|
end if;
|
||
|
end if;
|
||
|
end process;
|
||
|
|
||
|
-- MCR register
|
||
|
mcr_reg_w: process(clk)
|
||
|
begin
|
||
|
if rising_edge(clk) then
|
||
|
if rst = '1' then
|
||
|
reg_mcr <= "00000";
|
||
|
elsif reg_write = '1' and reg_idx = REG_IDX_MCR then
|
||
|
reg_mcr <= wb_dat_i(4 downto 0);
|
||
|
end if;
|
||
|
end if;
|
||
|
end process;
|
||
|
|
||
|
-- LSR register
|
||
|
lsr_reg_w: process(clk)
|
||
|
begin
|
||
|
if rising_edge(clk) then
|
||
|
if rst = '1' then
|
||
|
reg_lsr <= "00000000";
|
||
|
else
|
||
|
reg_lsr(REG_LSR_DR_BIT) <= data_in_pending;
|
||
|
|
||
|
-- Clear error bits on read. Those bits are
|
||
|
-- always 0 in sim for now.
|
||
|
-- if reg_read = '1' and reg_idx = REG_IDX_LSR then
|
||
|
-- reg_lsr(REG_LSR_OE_BIT) <= '0';
|
||
|
-- reg_lsr(REG_LSR_PE_BIT) <= '0';
|
||
|
-- reg_lsr(REG_LSR_FE_BIT) <= '0';
|
||
|
-- reg_lsr(REG_LSR_BI_BIT) <= '0';
|
||
|
-- reg_lsr(REG_LSR_FIFOE_BIT) <= '0';
|
||
|
-- end if;
|
||
|
|
||
|
-- Tx FIFO empty indicators. Always empty in sim
|
||
|
reg_lsr(REG_LSR_THRE_BIT) <= '1';
|
||
|
reg_lsr(REG_LSR_TEMT_BIT) <= '1';
|
||
|
end if;
|
||
|
end if;
|
||
|
end process;
|
||
|
|
||
|
-- MSR register
|
||
|
msr_reg_w: process(clk)
|
||
|
begin
|
||
|
if rising_edge(clk) then
|
||
|
if rst = '1' then
|
||
|
reg_msr <= "00000000";
|
||
|
elsif reg_read = '1' and reg_idx = REG_IDX_MSR then
|
||
|
reg_msr <= "00000000";
|
||
|
-- XXX TODO bit setting machine...
|
||
|
end if;
|
||
|
end if;
|
||
|
end process;
|
||
|
|
||
|
-- SCR register
|
||
|
scr_reg_w: process(clk)
|
||
|
begin
|
||
|
if rising_edge(clk) then
|
||
|
if rst = '1' then
|
||
|
reg_scr <= "00000000";
|
||
|
elsif reg_write = '1' and reg_idx = REG_IDX_SCR then
|
||
|
reg_scr <= wb_dat_i;
|
||
|
end if;
|
||
|
end if;
|
||
|
end process;
|
||
|
|
||
|
end architecture behaviour;
|