512 lines
24 KiB
VHDL
512 lines
24 KiB
VHDL
---------------------------------------------------------------------------------------------------------
|
|
--
|
|
-- Name: uart.vhd
|
|
-- Created: January 2019
|
|
-- Author(s): Philip Smart
|
|
-- Description: A simplistic UART fixed at 8N1 but with configurable baud rate and RX/TX Fifoś.
|
|
-- Credits: Originally using the simplistic UART as a guide, which was written by the following
|
|
-- authors:-
|
|
-- Philippe Carton, philippe.carton2 libertysurf.fr
|
|
-- Juan Pablo Daniel Borgna, jpdborgna gmail.com
|
|
-- Salvador E. Tropea, salvador inti.gob.ar
|
|
-- Copyright: (c) 2019 Philip Smart <philip.smart@net2net.org>
|
|
--
|
|
-- History: January 2019 - Initial module written using the simplistic UART as a guide but
|
|
-- adding cache and more control.
|
|
--
|
|
---------------------------------------------------------------------------------------------------------
|
|
-- This source file is free software: you can redistribute it and-or modify
|
|
-- it under the terms of the GNU General Public License as published
|
|
-- by the Free Software Foundation, either version 3 of the License, or
|
|
-- (at your option) any later version.
|
|
--
|
|
-- This source file is distributed in the hope that it will be useful,
|
|
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
-- GNU General Public License for more details.
|
|
--
|
|
-- You should have received a copy of the GNU General Public License
|
|
-- along with this program. If not, see <http:--www.gnu.org-licenses->.
|
|
---------------------------------------------------------------------------------------------------------
|
|
library ieee;
|
|
library pkgs;
|
|
library work;
|
|
use ieee.std_logic_1164.all;
|
|
use ieee.numeric_std.all;
|
|
use work.zpu_pkg.all;
|
|
use work.zpu_soc_pkg.all;
|
|
|
|
-- Based on the simplistic UART, handles 8N1 RS232 Rx/Tx with independent programmable baud rate and selectable FIFO buffers.
|
|
entity uart is
|
|
generic (
|
|
RX_FIFO_BIT_DEPTH : integer := 10;
|
|
TX_FIFO_BIT_DEPTH : integer := 8;
|
|
COUNTER_BITS : natural := 16;
|
|
BAUDCLK_FREQUENCY : integer := SYSTEM_FREQUENCY -- Baud rate clock frequency, used so a program can determine the clocking frequency and set divisor accordingly.
|
|
);
|
|
port (
|
|
-- CPU Interface
|
|
CLK : in std_logic; -- memory master clock
|
|
RESET : in std_logic; -- high active sync reset
|
|
ADDR : in std_logic_vector(1 downto 0); -- 0 = Read/Write Data, 1 = Control Register, 3 = Baud Register
|
|
DATA_IN : in std_logic_vector(wordSize-1 downto 0); -- write data
|
|
DATA_OUT : out std_logic_vector(wordSize-1 downto 0); -- read data
|
|
CS : in std_logic; -- Chip Select.
|
|
WREN : in std_logic; -- Write enable.
|
|
RDEN : in std_logic; -- Read enable.
|
|
|
|
-- IRQ outputs
|
|
TXINTR : out std_logic; -- Tx buffer empty interrupt.
|
|
RXINTR : out std_logic; -- Rx buffer full interrupt.
|
|
|
|
-- Serial data
|
|
TXD : out std_logic;
|
|
RXD : in std_logic
|
|
);
|
|
end uart;
|
|
|
|
architecture rtl of uart is
|
|
|
|
signal RXD_SYNC : std_logic;
|
|
signal RXD_SYNC2 : std_logic;
|
|
|
|
type RXSTATES is (idle, start, bits, stop);
|
|
signal RX_STATE : RXSTATES := idle;
|
|
|
|
signal RX_CLOCK_DIVISOR : unsigned(COUNTER_BITS-1 downto 0) := X"043D"; -- Main clock divisor to create RX Clock.
|
|
signal RX_COUNTER : unsigned(COUNTER_BITS-1 downto 0); -- RX Clock generator counter.
|
|
signal RX_CLOCK : std_logic; -- RX Clock.
|
|
signal RX_BUFFER : std_logic_vector(8 downto 0); -- Receive deserialisation buffer.
|
|
signal RX_DATA : std_logic_vector(7 downto 0); -- Received data holding register.
|
|
signal RX_DATA_READY : std_logic; -- Byte available to read = 1
|
|
signal RX_OVERRUN : std_logic; -- New byte received before previous read by CPU, old value lost.
|
|
signal RX_INTR : std_logic; -- Rx buffer full interrupt.
|
|
signal RX_ENABLE : std_logic; -- Enable RX unit.
|
|
signal RX_ENABLE_FIFO : std_logic; -- Enable RX FIFO.
|
|
signal RX_RESET : std_logic; -- Reset RX unit.
|
|
signal RX_FIFO_EMPTY : std_logic; -- RX FIFO is empty = 1.
|
|
signal RX_FIFO_FULL : std_logic; -- RX FIFO is full = 1.
|
|
|
|
type TXSTATES is (idle, bits);
|
|
signal TX_STATE : TXSTATES := idle;
|
|
|
|
signal TX_CLOCK_DIVISOR : unsigned(COUNTER_BITS-1 downto 0) := X"043D"; -- Main clock divisor to create TX Clock.
|
|
signal TX_BUFFER : std_logic_vector(17 downto 0); -- Transmit serialisation buffer.
|
|
signal TX_DATA : std_logic_vector(7 downto 0); -- Transmit holding register.
|
|
signal TX_DATA_LOADED : std_logic; -- Data loaded into transmit buffer.
|
|
signal TX_BUSY : std_logic; -- Transmit in progress.
|
|
signal TX_OVERRUN : std_logic; -- TX write when last byte not sent or fifo full.
|
|
signal TX_COUNTER : unsigned(COUNTER_BITS-1 downto 0); -- TX Clock generator counter.
|
|
signal TX_CLOCK : std_logic; -- TX Clock.
|
|
signal TX_INTR : std_logic; -- Tx buffer empty interrupt.
|
|
signal TX_ENABLE : std_logic; -- Enable TX unit.
|
|
signal TX_ENABLE_FIFO : std_logic; -- Enable TX FIFO.
|
|
signal TX_RESET : std_logic; -- Reset TX unit.
|
|
signal TX_FIFO_EMPTY : std_logic; -- TX FIFO is empty = 1.
|
|
signal TX_FIFO_FULL : std_logic; -- TX FIFO is full = 1.
|
|
|
|
-- FIFO buffers.
|
|
type RX_MEM_T is array (0 to ((2**RX_FIFO_BIT_DEPTH)-1)) of std_logic_vector(7 downto 0);
|
|
type TX_MEM_T is array (0 to ((2**TX_FIFO_BIT_DEPTH)-1)) of std_logic_vector(7 downto 0);
|
|
signal RX_FIFO : RX_MEM_T;
|
|
signal TX_FIFO : TX_MEM_T;
|
|
-- RX Fifo address pointers.
|
|
signal RX_FIFO_WR_ADDR : unsigned(RX_FIFO_BIT_DEPTH-1 downto 0);
|
|
signal RX_FIFO_RD_ADDR : unsigned(RX_FIFO_BIT_DEPTH-1 downto 0);
|
|
-- TX Fifo address pointers.
|
|
signal TX_FIFO_WR_ADDR : unsigned(TX_FIFO_BIT_DEPTH-1 downto 0);
|
|
signal TX_FIFO_RD_ADDR : unsigned(TX_FIFO_BIT_DEPTH-1 downto 0);
|
|
|
|
begin
|
|
|
|
-- Signal synchronisation for rxd.
|
|
-- Without this, the state machine can get messed up. The change from one state
|
|
-- to another is not an atomic operation; leaving one state and entering the next
|
|
-- are distinct, and it's possible (and, in fact, common) for one
|
|
-- to happen without the other if inputs aren't properly synchronised.
|
|
process(CLK, RXD, RX_ENABLE)
|
|
begin
|
|
if RX_ENABLE = '1' and rising_edge(CLK) then
|
|
RXD_SYNC2 <= RXD;
|
|
RXD_SYNC <= RXD_SYNC2;
|
|
end if;
|
|
end process;
|
|
|
|
|
|
-- Clock generators.
|
|
-- We have independent Rx and Tx clocks, generated from counters which count down from clock_divisor to zero.
|
|
-- At zero, we generate a momentary high pulse which is used as the serial clock signal.
|
|
|
|
-- Tx Clock generation
|
|
-- Very simple - the counter is reset when either it reaches zero or
|
|
-- the Tx is idle, and counts down once per system clock tick.
|
|
process(CLK, TX_ENABLE, TX_CLOCK_DIVISOR)
|
|
begin
|
|
if TX_ENABLE = '1' and rising_edge(CLK) then
|
|
TX_CLOCK <= '0';
|
|
|
|
if TX_STATE = idle then
|
|
TX_COUNTER <= TX_CLOCK_DIVISOR;
|
|
else
|
|
TX_COUNTER <= TX_COUNTER-1;
|
|
if TX_COUNTER = 0 then
|
|
TX_CLOCK <= '1';
|
|
TX_COUNTER <= TX_CLOCK_DIVISOR;
|
|
end if;
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
|
|
-- Rx Clock generation
|
|
-- The Rx clock is slightly more complicated. When idle we detect the leading edge of the
|
|
-- start bit, and set the counter to half a bit width. When it reaches zero, the counter is
|
|
-- set to a full bit width, so clock ticks should land in the centre of each bit.
|
|
process(clk,RXD_SYNC,RX_COUNTER,RX_STATE,RX_ENABLE)
|
|
begin
|
|
if RX_ENABLE = '1' and rising_edge(clk) then
|
|
RX_CLOCK <= '0';
|
|
|
|
if RX_STATE=idle then
|
|
if RXD_SYNC='0' then -- Start bit? Set counter to half a bit width
|
|
RX_COUNTER <= '0' & RX_CLOCK_DIVISOR(COUNTER_BITS-1 downto 1);
|
|
end if;
|
|
else
|
|
RX_COUNTER<=RX_COUNTER-1;
|
|
if RX_COUNTER=0 then
|
|
RX_CLOCK <= '1';
|
|
RX_COUNTER <= RX_CLOCK_DIVISOR;
|
|
end if;
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
|
|
-- Data Rx
|
|
-- We use a 9-bit shift register here. Upon detection of the start bit, we
|
|
-- load the shift register with "100000000".
|
|
-- As each bit is received we shift the register one bit to the right, and load new data
|
|
-- into bit 8.
|
|
-- When the 1 initially in bit 8 reaches bit zero we know we've received the entire word.
|
|
process(clk,RX_RESET,RXD_SYNC,RX_STATE,RX_FIFO_RD_ADDR,RX_FIFO_WR_ADDR,RX_ENABLE,RX_INTR)
|
|
variable RX_FULL_V : std_logic;
|
|
variable RX_EMPTY_V : std_logic;
|
|
begin
|
|
|
|
if RX_RESET='1' then
|
|
RX_STATE <= idle;
|
|
RX_INTR <= '0';
|
|
RX_DATA_READY <= '0';
|
|
RX_OVERRUN <= '0';
|
|
RX_FIFO_WR_ADDR <= (others => '0');
|
|
RX_FIFO_RD_ADDR <= (others => '0');
|
|
RX_FIFO_EMPTY <= '1';
|
|
RX_FIFO_FULL <= '0';
|
|
|
|
elsif RX_ENABLE = '1' and rising_edge(clk) then
|
|
|
|
-- Interrupts only last 1 clock cycle, clear any active interrupt.
|
|
RX_INTR <= '0';
|
|
|
|
-- When Read and Write FIFO addresses are equal, FIFO is empty.
|
|
if RX_FIFO_RD_ADDR = RX_FIFO_WR_ADDR then
|
|
RX_EMPTY_V := '1';
|
|
else
|
|
RX_EMPTY_V := '0';
|
|
end if;
|
|
|
|
-- When Write address is 1 behind the read address, FIFO is full.
|
|
if RX_FIFO_WR_ADDR = RX_FIFO_RD_ADDR-1 then
|
|
RX_FULL_V := '1';
|
|
else
|
|
RX_FULL_V := '0';
|
|
end if;
|
|
|
|
-- If CPU requests to read data, clear the DATA_READY flag.
|
|
--
|
|
if CS = '1' and RDEN = '1' and ADDR = "00" and RX_DATA_READY = '1' then
|
|
RX_DATA_READY <= '0';
|
|
end if;
|
|
|
|
-- If fifo enabled and RX_DATA register is empty, pop the next byte off the stack for the CPU to read.
|
|
--
|
|
if RX_ENABLE_FIFO = '1' and RX_DATA_READY = '0' and RX_EMPTY_V ='0' then
|
|
RX_DATA <= RX_FIFO( to_integer(RX_FIFO_RD_ADDR) );
|
|
RX_FIFO_RD_ADDR <= RX_FIFO_RD_ADDR + 1;
|
|
RX_DATA_READY <= '1';
|
|
end if;
|
|
|
|
case RX_STATE is
|
|
when idle =>
|
|
if RXD_SYNC='0' then
|
|
RX_STATE <= start;
|
|
end if;
|
|
when start =>
|
|
if RX_CLOCK='1' then
|
|
if RXD_SYNC='0' then
|
|
RX_BUFFER <= "100000000"; -- Set marker bit.
|
|
RX_STATE <= bits;
|
|
else
|
|
RX_STATE <= idle;
|
|
end if;
|
|
end if;
|
|
when bits =>
|
|
if RX_CLOCK='1' then
|
|
RX_BUFFER <= RXD_SYNC & RX_BUFFER(8 downto 1);
|
|
end if;
|
|
if RX_BUFFER(0)='1' then -- Marker bit has reached bit 0
|
|
RX_STATE <= stop;
|
|
end if;
|
|
when stop =>
|
|
if RX_CLOCK='1' then
|
|
if RXD_SYNC='1' then -- valid stop bit?
|
|
|
|
-- If fifo enabled and space available, write otherwise discard.
|
|
if RX_ENABLE_FIFO = '1' then
|
|
if RX_FULL_V = '0' then
|
|
RX_FIFO(to_integer(RX_FIFO_WR_ADDR)) <= RX_BUFFER(8 downto 1);
|
|
RX_FIFO_WR_ADDR <= RX_FIFO_WR_ADDR + 1;
|
|
else
|
|
RX_OVERRUN <= '1';
|
|
end if;
|
|
|
|
-- Interrupt if first byte or buffer becoming full.
|
|
if (RX_EMPTY_V = '1' and RX_DATA_READY = '0') or RX_FIFO_WR_ADDR = RX_FIFO_RD_ADDR - 2 then
|
|
RX_INTR <= '1';
|
|
end if;
|
|
else
|
|
if RX_DATA_READY = '0' then
|
|
RX_DATA <= RX_BUFFER(8 downto 1);
|
|
RX_DATA_READY <= '1';
|
|
else
|
|
RX_OVERRUN <= '1';
|
|
end if;
|
|
|
|
-- Always interrupt if fifo disabled.
|
|
RX_INTR <= '1';
|
|
end if;
|
|
end if;
|
|
RX_STATE <= idle;
|
|
end if;
|
|
when others =>
|
|
RX_STATE <= idle;
|
|
end case;
|
|
end if;
|
|
|
|
-- Put variables onto external signals.
|
|
RX_FIFO_EMPTY <= RX_EMPTY_V;
|
|
RX_FIFO_FULL <= RX_FULL_V;
|
|
|
|
-- Put internal interrupt status onto bus.
|
|
RXINTR <= RX_INTR;
|
|
end process;
|
|
|
|
-- Process to read data from the receive buffer/fifo when selected.
|
|
process(clk, RX_RESET, RX_ENABLE)
|
|
begin
|
|
if RX_RESET = '1' then
|
|
|
|
elsif RX_ENABLE = '1' and rising_edge(clk) then
|
|
|
|
end if;
|
|
end process;
|
|
|
|
|
|
-- Data Tx
|
|
-- Similarly to the Rx routine, we use a shift register larger than the word,
|
|
-- which also includes a marker bit. This time the marker bit is a zero, and when
|
|
-- the zero reaches bit 8, we know we've transmitted the entire word plus one stop bit.
|
|
process(clk,TX_RESET,TX_STATE,TX_FIFO_RD_ADDR,TX_FIFO_WR_ADDR,TX_ENABLE,TX_INTR)
|
|
variable TX_FULL_V : std_logic;
|
|
variable TX_EMPTY_V : std_logic;
|
|
begin
|
|
if TX_FIFO_RD_ADDR=TX_FIFO_WR_ADDR then
|
|
TX_EMPTY_V := '1';
|
|
else
|
|
TX_EMPTY_V := '0';
|
|
end if;
|
|
|
|
if TX_FIFO_WR_ADDR = TX_FIFO_RD_ADDR-1 then
|
|
TX_FULL_V := '1';
|
|
else
|
|
TX_FULL_V := '0';
|
|
end if;
|
|
|
|
if TX_RESET='1' then
|
|
TX_STATE <= idle;
|
|
TX_BUSY <= '0';
|
|
TX_DATA_LOADED <= '0';
|
|
TXD <= '1';
|
|
TX_INTR <= '0';
|
|
TX_OVERRUN <= '0';
|
|
TX_FIFO_WR_ADDR <= (others => '0');
|
|
TX_FIFO_RD_ADDR <= (others => '0');
|
|
TX_FIFO_EMPTY <= '1';
|
|
TX_FIFO_FULL <= '0';
|
|
|
|
elsif TX_ENABLE = '1' and rising_edge(clk) then
|
|
|
|
TX_INTR <= '0';
|
|
|
|
-- If CPU writes data, load into FIFO or direct into TX Data register.
|
|
--
|
|
if CS = '1' and WREN = '1' and ADDR = "00" then
|
|
-- Store data in FIFO if enabled and not full.
|
|
--
|
|
if TX_ENABLE_FIFO = '1' then
|
|
if TX_FULL_V = '0' then
|
|
TX_FIFO(to_integer(TX_FIFO_WR_ADDR)) <= DATA_IN(7 downto 0);
|
|
TX_FIFO_WR_ADDR<= TX_FIFO_WR_ADDR + 1;
|
|
else
|
|
TX_OVERRUN <= '1';
|
|
end if;
|
|
else
|
|
-- Else load TX Data register with data.
|
|
if TX_DATA_LOADED = '0' then
|
|
TX_DATA <= DATA_IN(7 downto 0);
|
|
TX_DATA_LOADED <= '1';
|
|
else
|
|
TX_OVERRUN <= '1';
|
|
end if;
|
|
end if;
|
|
end if;
|
|
|
|
-- If FIFO enabled, pop the next byte into the TX holding register.
|
|
if TX_ENABLE_FIFO = '1' and TX_DATA_LOADED = '0' and TX_EMPTY_V = '0' then
|
|
TX_DATA <= TX_FIFO(to_integer(TX_FIFO_RD_ADDR));
|
|
TX_FIFO_RD_ADDR <= TX_FIFO_RD_ADDR + 1;
|
|
TX_DATA_LOADED <= '1';
|
|
end if;
|
|
|
|
-- TX state machine, serialise the TX buffer.
|
|
case TX_STATE is
|
|
when idle =>
|
|
-- If data loaded into the TX holding register and we are at idle (ie last byte transmitted),
|
|
-- load into the transmit buffer and commence transmission.
|
|
--
|
|
if TX_DATA_LOADED = '1' then
|
|
TX_BUFFER <="0111111111" & TX_DATA; -- marker bit + data
|
|
TX_STATE <=bits;
|
|
TX_BUSY <='1';
|
|
TXD <='0'; -- Start bit
|
|
TX_DATA_LOADED <= '0';
|
|
end if;
|
|
when bits =>
|
|
if TX_CLOCK='1' then
|
|
txd <= TX_BUFFER(0);
|
|
TX_BUFFER <= '0' & TX_BUFFER(17 downto 1);
|
|
|
|
if TX_BUFFER(8) = '0' then -- Marker bit has reached bit 8
|
|
TX_STATE <= idle;
|
|
TX_BUSY <= '0';
|
|
|
|
-- Interrupt if there is no data loaded into holding register, either from fifo or direct.
|
|
if TX_DATA_LOADED = '0' then
|
|
TX_INTR<= '1';
|
|
end if;
|
|
end if;
|
|
end if;
|
|
when others =>
|
|
TX_STATE <=idle;
|
|
end case;
|
|
end if;
|
|
|
|
-- Put variables onto external signals.
|
|
TX_FIFO_EMPTY <= TX_EMPTY_V;
|
|
TX_FIFO_FULL <= TX_FULL_V;
|
|
|
|
-- Put internal interrupt status onto bus.
|
|
TXINTR <= TX_INTR;
|
|
end process;
|
|
|
|
-- Process to pack the data and status onto a buffer ready to be read by the CPU.
|
|
--
|
|
process(ADDR, RX_FIFO_EMPTY, RX_FIFO_FULL, RX_DATA_READY, RX_OVERRUN, RX_INTR, RX_ENABLE_FIFO, RX_ENABLE, RX_RESET,
|
|
TX_FIFO_EMPTY, TX_FIFO_FULL, TX_BUSY, TX_DATA_LOADED, TX_OVERRUN, TX_INTR, TX_ENABLE_FIFO, TX_ENABLE, TX_RESET,
|
|
TX_CLOCK_DIVISOR, RX_CLOCK_DIVISOR, RX_DATA, RX_FIFO_RD_ADDR, RX_FIFO_WR_ADDR, TX_FIFO_RD_ADDR, TX_FIFO_WR_ADDR)
|
|
begin
|
|
case ADDR is
|
|
when "00" =>
|
|
DATA_OUT <= X"000000" & RX_DATA;
|
|
|
|
-- Status.
|
|
when "01" =>
|
|
DATA_OUT <= (others => '0');
|
|
DATA_OUT(0) <= RX_FIFO_EMPTY; -- RX Fifo empty = 1
|
|
DATA_OUT(1) <= RX_FIFO_FULL; -- RX Fifo full = 1
|
|
DATA_OUT(2) <= RX_DATA_READY; -- RX Byte received in holding register = 1
|
|
DATA_OUT(3) <= RX_OVERRUN; -- RX received next data before last was read = 1
|
|
DATA_OUT(4) <= RX_INTR; -- RX Interrupt = 1
|
|
DATA_OUT(5) <= RX_ENABLE_FIFO; -- RX Fifo enabled = 1
|
|
DATA_OUT(6) <= RX_ENABLE; -- RX enabled = 1
|
|
DATA_OUT(7) <= RX_RESET; -- RX is in reset = 1
|
|
-- TX Shadow copy, non invasive.
|
|
DATA_OUT(16+0) <= TX_FIFO_EMPTY; -- TX Idle = 1
|
|
DATA_OUT(16+1) <= TX_FIFO_FULL; -- TX Fifo full = 1
|
|
DATA_OUT(16+2) <= TX_BUSY; -- TX Busy serialising = 1
|
|
DATA_OUT(16+3) <= TX_DATA_LOADED; -- TX data loaded into holding register = 1
|
|
DATA_OUT(16+4) <= TX_OVERRUN; -- TX written to when last byte not sent or fifo full.
|
|
DATA_OUT(16+5) <= TX_INTR; -- TX Interrupt = 1
|
|
DATA_OUT(16+6) <= TX_ENABLE_FIFO; -- TX Fifo enabled = 1
|
|
DATA_OUT(16+7) <= TX_ENABLE; -- TX enabled = 1
|
|
DATA_OUT(16+8) <= TX_RESET; -- TX is in reset = 1
|
|
|
|
-- FIFO Status.
|
|
when "10" =>
|
|
DATA_OUT <= (others => '0');
|
|
DATA_OUT( RX_FIFO_BIT_DEPTH-1 downto 0) <= std_logic_vector(RX_FIFO_WR_ADDR - RX_FIFO_RD_ADDR);
|
|
DATA_OUT(16+TX_FIFO_BIT_DEPTH-1 downto 16) <= std_logic_vector(TX_FIFO_WR_ADDR - TX_FIFO_RD_ADDR);
|
|
|
|
-- System clock frequency.
|
|
when "11" =>
|
|
DATA_OUT <= std_logic_vector(to_unsigned(BAUDCLK_FREQUENCY, wordSize));
|
|
end case;
|
|
end process;
|
|
|
|
-- Write process. Accept data from the CPU and program the unit accordingly.
|
|
process(CLK,RESET)
|
|
begin
|
|
if RESET='1' then
|
|
RX_CLOCK_DIVISOR <= X"043D"; -- Default 115200 assuming 100MHz clock.
|
|
TX_CLOCK_DIVISOR <= X"043D";
|
|
RX_ENABLE <= '1';
|
|
TX_ENABLE <= '1';
|
|
RX_ENABLE_FIFO <= '1';
|
|
TX_ENABLE_FIFO <= '1';
|
|
RX_RESET <= '1';
|
|
TX_RESET <= '1';
|
|
|
|
elsif rising_edge(CLK) then
|
|
RX_RESET <= '0';
|
|
TX_RESET <= '0';
|
|
if CS = '1' and WREN = '1' then
|
|
case ADDR is
|
|
-- Data, written direct to fifo or TX unit.
|
|
when "00" =>
|
|
|
|
-- RX/TX Control
|
|
when "01" => -- RX CTL
|
|
RX_ENABLE <= DATA_IN(0);
|
|
RX_ENABLE_FIFO <= DATA_IN(1);
|
|
RX_RESET <= DATA_IN(2);
|
|
--
|
|
TX_ENABLE <= DATA_IN(16+0);
|
|
TX_ENABLE_FIFO <= DATA_IN(16+1);
|
|
TX_RESET <= DATA_IN(16+2);
|
|
|
|
-- Unused
|
|
when "10" =>
|
|
|
|
-- Baud Rate Generate setup.
|
|
when "11" =>
|
|
RX_CLOCK_DIVISOR <= unsigned(DATA_IN(15 downto 0));
|
|
TX_CLOCK_DIVISOR <= unsigned(DATA_IN(31 downto 16));
|
|
RX_RESET <= '1';
|
|
TX_RESET <= '1';
|
|
end case;
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
end architecture;
|