forked from cores/microwatt

2 changed files with 423 additions and 1 deletions
@ -0,0 +1,421 @@
@@ -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; |
Loading…
Reference in new issue