📄 uart.vhd
字号:
-----------------------------------------------------------------------------
-- This file is a part of the LEON VHDL model
-- Copyright (C) 1999 European Space Agency (ESA)
--
-- This program 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 2 of the License, or
-- (at your option) any later version.
--
-- See the file COPYING for the full details of the license.
-----------------------------------------------------------------------------
-- Entity: uart
-- File: uart.vhd
-- Author: Jiri Gaisler - ESA/ESTEC
-- Description: Asynchronous UART. Implements 8-bit data frame with one
-- stop-bit. Programmable options:
-- * parity bit (on/off)
-- * parity polarity (odd/even)
-- * baud-rate (12-bit programmable divider)
-- * hardware flow-control (CTS/RTS)
-- * Loop-back testing
-- Error-detection in receiver detects parity, framing
-- break and overrun errors.
------------------------------------------------------------------------------
-- Version control:
-- 11-10-1998: First implemetation
-- 26-09-1999: Release 1.0
------------------------------------------------------------------------------
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.std_logic_unsigned."+";
use IEEE.std_logic_unsigned."-";
use work.config.all;
use work.iface.all;
use work.macro.all;
entity uart is
generic (id : integer range 0 to 3 := 0);
port (
rst : in std_logic;
clk : in std_logic;
pbi : in pbus_in_type;
pbo : out pbus_out_type;
uarti : in uart_in_type;
uarto : out uart_out_type
);
end;
architecture rtl of uart is
type txfsm is (idle, data, parity, stopbit);
type rxfsm is (idle, startbit, data, parity, stopbit);
type uartregs is record
rxen : std_logic; -- receiver enabled
txen : std_logic; -- transmitter enabled
rirqen : std_logic; -- receiver irq enable
tirqen : std_logic; -- transmitter irq enable
parsel : std_logic; -- parity select
paren : std_logic; -- parity select
flow : std_logic; -- flow control enable
loopb : std_logic; -- loop back mode enable
dready : std_logic; -- data ready
rsempty : std_logic; -- receiver shift register empty (internal)
tsempty : std_logic; -- transmitter shift register empty
thempty : std_logic; -- transmitter hold register empty
break : std_logic; -- break detected
ovf : std_logic; -- receiver overflow
parerr : std_logic; -- parity error
frame : std_logic; -- framing error
rtsn : std_logic; -- request to send
rhold : std_logic_vector(7 downto 0);
rshift : std_logic_vector(7 downto 0);
tshift : std_logic_vector(10 downto 0);
thold : std_logic_vector(7 downto 0);
irq : std_logic; -- tx/rx interrupt (internal)
tpar : std_logic; -- tx data parity (internal)
txstate : txfsm;
txclk : std_logic_vector(2 downto 0); -- tx clock divider
txtick : std_logic; -- tx clock (internal)
rxstate : rxfsm;
rxclk : std_logic_vector(2 downto 0); -- rx clock divider
rxdb : std_logic; -- rx data filtering buffer
dpar : std_logic; -- rx data parity (internal)
rxtick : std_logic; -- rx clock (internal)
tick : std_logic; -- rx clock (internal)
scaler : std_logic_vector(11 downto 0);
brate : std_logic_vector(11 downto 0);
end record;
signal r, rin : uartregs;
begin
uartop : process(rst, r, pbi, uarti)
variable rdata : std_logic_vector(31 downto 0);
variable scaler : std_logic_vector(11 downto 0);
variable rxclk, txclk : std_logic_vector(2 downto 0);
variable regaddr : std_logic_vector(3 downto 0);
variable rxd : std_logic;
variable v : uartregs;
begin
v := r;
v.irq := '0'; v.txtick := '0'; v.rxtick := '0'; v.tick := '0';
-- uart address decoding
if id = 0 then
regaddr := UART0ADDR;
else
regaddr := UART1ADDR;
end if;
-- scaler
-- pragma translate_off
if not is_x(r.scaler) then -- avoid warnings at reset time
-- pragma translate_on
scaler := r.scaler - 1;
-- pragma translate_off
end if;
-- pragma translate_on
if (r.rxen or r.txen) = '1' then
v.scaler := scaler;
v.tick := scaler(11) and not r.scaler(11);
if v.tick = '1' then v.scaler := r.brate; end if;
end if;
-- read/write registers
rdata := (others => '0');
if (pbi.enable = '1') and (pbi.address(5 downto 2) = regaddr) then
if pbi.read = '1' then
case pbi.address(1 downto 0) is
when "00" =>
rdata(7 downto 0) := r.rhold; v.dready := '0';
when "01" =>
rdata(6 downto 0) := r.frame & r.parerr & r.ovf &
r.break & r.thempty & r.tsempty & r.dready;
when "10" =>
rdata(7 downto 0) := r.loopb & r.flow & r.paren & r.parsel &
r.tirqen & r.rirqen & r.txen & r.rxen;
when "11" =>
rdata(11 downto 0) := r.scaler;
when others => null;
end case;
else
case pbi.address(1 downto 0) is
when "01" =>
v.frame := pbi.data(6);
v.parerr := pbi.data(5);
v.ovf := pbi.data(4);
v.break := pbi.data(3);
when "10" =>
v.loopb := pbi.data(7);
v.flow := pbi.data(6);
v.paren := pbi.data(5);
v.parsel := pbi.data(4);
v.tirqen := pbi.data(3);
v.rirqen := pbi.data(2);
v.txen := pbi.data(1);
v.rxen := pbi.data(0);
when "11" =>
v.brate := pbi.data(11 downto 0);
v.scaler := pbi.data(11 downto 0);
when others => null;
end case;
end if;
end if;
-- tx clock
-- pragma translate_off
if not is_x(r.txclk) then -- avoid warnings at reset time
-- pragma translate_on
txclk := r.txclk + 1;
-- pragma translate_off
else
txclk := (others => 'X');
end if;
-- pragma translate_on
if r.tick = '1' then
v.txclk := txclk;
v.txtick := r.txclk(2) and not txclk(2);
end if;
-- rx clock
-- pragma translate_off
if not is_x(r.rxclk) then -- avoid warnings at reset time
-- pragma translate_on
rxclk := r.rxclk + 1;
-- pragma translate_off
else
rxclk := (others => 'X');
end if;
-- pragma translate_on
if r.tick = '1' then
v.rxclk := rxclk;
v.rxtick := r.rxclk(2) and not rxclk(2);
end if;
-- filter rx data
if r.loopb = '1' then v.rxdb := r.tshift(0);
else v.rxdb := uarti.rxd; end if;
rxd := r.rxdb;
-- transmitter operation
case r.txstate is
when idle => -- idle state
if ((r.txen and (not r.thempty) and r.txtick) and
((not uarti.ctsn) or not r.flow)) = '1' then
v.tshift := "10" & r.thold & '0'; v.txstate := data;
v.tpar := r.parsel; v.irq := r.tirqen; v.thempty := '1';
v.tsempty := '0'; v.txclk := "001"; v.txtick := '0';
end if;
when data => -- transmitt data frame
if r.txtick = '1' then
v.tpar := r.tpar xor r.tshift(1);
v.tshift := '1' & r.tshift(10 downto 1);
if r.tshift(10 downto 1) = "1111111110" then
if r.paren = '1' then
v.tshift(0) := r.tpar; v.txstate := parity;
else
v.tshift(0) := '1'; v.txstate := stopbit;
end if;
end if;
end if;
when parity => -- transmitt parity bit
if r.txtick = '1' then
v.tshift := '1' & r.tshift(10 downto 1); v.txstate := stopbit;
end if;
when stopbit => -- transmitt stop bit
if r.txtick = '1' then
v.tshift := '1' & r.tshift(10 downto 1); v.txstate := idle;
v.tsempty := '1';
end if;
end case;
-- writing of tx data register must be done after tx fsm to get correct
-- operation of thempty flag
if (pbi.enable = '1') and (pbi.address(5 downto 2) = regaddr) then
if pbi.read = '0' then
case pbi.address(1 downto 0) is
when "00" =>
v.thold := pbi.data(7 downto 0); v.thempty := '0'; v.irq := '0';
when others => null;
end case;
end if;
end if;
-- receiver operation
case r.rxstate is
when idle => -- wait for start bit
if ((not r.rsempty) and not r.dready) = '1' then
v.rhold := r.rshift; v.rsempty := '1'; v.dready := '1';
end if;
if (r.rxen and (not rxd)) = '1' then
v.rxstate := startbit; v.rshift := (others => '1'); v.rxclk := "100";
if v.rsempty = '0' then v.ovf := '1'; end if;
v.rsempty := '0'; v.rxtick := '0';
end if;
when startbit => -- check validity of start bit
if r.rxtick = '1' then
if rxd = '0' then
v.rshift := rxd & r.rshift(7 downto 1); v.rxstate := data;
v.dpar := r.parsel;
else
v.rxstate := idle;
end if;
end if;
when data => -- receive data frame
if r.rxtick = '1' then
v.dpar := r.dpar xor rxd;
v.rshift := rxd & r.rshift(7 downto 1);
if r.rshift(0) = '0' then
if r.paren = '1' then v.rxstate := parity;
else
v.rxstate := stopbit; v.dpar := '0';
end if;
end if;
end if;
when parity => -- receive parity bit
if r.rxtick = '1' then
v.dpar := r.dpar xor rxd;
v.rxstate := stopbit;
end if;
when stopbit => -- receive stop bit
if r.rxtick = '1' then
v.irq := r.tirqen;
if rxd = '1' then
v.parerr := r.dpar; v.dready := not r.dpar;
v.rsempty := r.dpar;
if (v.dready and not r.dready) = '1' then
v.rhold := r.rshift; v.rsempty := '1';
end if;
else
if r.rshift = "00000000" then
v.break := '1'; -- break
else
v.frame := '1'; -- framing error
end if;
v.rsempty := '1';
end if;
v.rxstate := idle;
end if;
end case;
if r.rxtick = '1' then v.rtsn := r.dready and not r.rsempty; end if;
-- reset operation
if rst = '0' then
v.frame := '0'; v.rsempty := '1';
v.parerr := '0'; v.ovf := '0'; v.break := '0'; v.thempty := '1';
v.tsempty := '1'; v.dready := '0'; v.txen := '0'; v.rxen := '0';
v.txstate := idle; v.rxstate := idle; v.tshift(0) := '1';
v.scaler := (others => '0'); v.brate := (others => '0');
v.txclk := (others => '0'); v.rxclk := (others => '0');
v.rtsn := '1'; v.flow := '0';
end if;
-- update registers
rin <= v;
-- drive outputs
uarto.txd <= r.tshift(0) or r.loopb;
uarto.irq <= r.irq;
uarto.flow <= r.flow;
uarto.rtsn <= r.rtsn;
uarto.txen <= r.txen;
uarto.rxen <= r.rxen;
pbo.data <= rdata;
end process;
regs : process(clk)
begin
if clk'event and (clk = '1') then
r <= rin;
end if;
end process;
end;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -