Merge pull request #213 from ozbenh/uart16550
Add support for standard 16550 style UARTpull/219/head
commit
57604c1a6e
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,421 @@
|
|||||||
|
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;
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue