You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
421 lines
15 KiB
421 lines
15 KiB
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;
|
|
|