--- /dev/null
+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;