📄 uart.vhd
字号:
-- EE301B Semester Project, Winter 1998
-- 8051 UART (Universal Asynchronous Receiver/Transmitter) model
-- Source : Intel 8051 Family Microprocessors Manual
-- Author : Lingfeng Yuan, Prajakta Kurvey
-- Revision history:
-- 04/05
-- Started writing the code for the UART
-- 04/08
-- implemented the transmission and reception of mode 0 and 1
-- still a lot of errors and assumptions
-- 04/09
-- finished mode 2 and 3
-- looks correct except for the assumptions
-- 19/09
-- changed the divide-by-16 counters
-- 23/09
-- changed the transmitter and receiver processes to be sentitive to a list
-- eliminated all wait statements in them
-- eliminated all bus contention problems
-- package to define some types
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
package mc8051_UART_pkg is
subtype byte is unsigned (7 downto 0);
constant sbuf_addr : byte := "10011001";
constant scon_addr : byte := "10011000";
constant all_0 : byte := "00000000";
constant hi_imp : byte := "ZZZZZZZZ";
-- the following type is used to break up the machine cycle
-- into 6 states, with 2 pulses for each state
-- copied from Mayer's program
-- type machine_cycle_states is (init, s1p1, s1p2, s2p1, s2p2, s3p1, s3p2,
--s4p1, s4p2, s5p1, s5p2, s6p1, s6p2);
end mc8051_UART_pkg;
-- main program for the UART
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use work.mc8051_UART_pkg.all;
use work.pack8051.all;
-- these are the interface signals for our stand-alone UART
entity mc8051_UART is
port (
cycle_state : in machine_cycle_states;
p2clk : in std_logic; -- phase 2 clock
addr_gb : in byte; -- global address bus
data_gb : inout byte := hi_imp; -- global data bus
rd_gb : in std_logic; -- global read signal, active high
wr_gb : in std_logic; -- global write signal, active high
indirect_sel : in std_logic; -- direct or indirect address mode
tf1 : in std_logic := '0'; -- tf1 is the timer 1 overflow flag
smod : in std_logic := '0'; -- smod is pcon.7
scon_out : out byte; -- serial port control SFR
sbuf_out : out byte;
rxd : inout std_logic := 'H';
txd : out std_logic := '1';
acknow : out std_logic := 'L');
end entity;
architecture behave of mc8051_UART is
signal to_send, write_to_sbuf : std_logic := '0';
-- serial control - should it be all_0 at default?
signal scon : unsigned(7 DOWNTO 0) := (OTHERS => '0');
signal tb8_set, tb8_reset : std_logic := '0';
signal rb8_set, rb8_reset : std_logic := '0';
signal ti_set, ri_set : std_logic := '0';
-- some aliases to decompose scon
-- sm_r is reversed serial port mode OR IS IT? -MRM
alias sm_r : unsigned (2 downto 0) is scon(7 downto 5);
alias ren : std_logic is scon(4);
alias tb8 : std_logic is scon(3);
alias rb8 : std_logic is scon(2);
alias ti : std_logic is scon(1);
alias ri : std_logic is scon(0);
-- serial port buffers
signal sbuf_wr, sbuf_rd : byte := all_0;
-- divide-by-16 counters and their reset signals (rising edge sensitive)
-- tf1_16 is for timer 1 overflow signal
-- p2clk_16 is for phase 2 clock
subtype cnt_type is natural range 0 to 16;
signal tf1_16, rf1_16, p2clk_16 : cnt_type := 0;
signal tf1_16_reset, rf1_16_reset, p2clk_16_reset : std_logic := '0';
signal acknow_wr, acknow_rd : std_logic := 'L';
signal txd_wr, txd_rd : std_logic := '1';
begin
sbuf_out <= sbuf_rd;
scon_out <= scon;
-- process to detect write_to_sbuf operation
-- we still need to verify the timing of the execution of the instruction
process begin
wait until wr_gb = '1';
if addr_gb = sbuf_addr and indirect_sel = '0' then -- CPU writes to serial port
sbuf_wr <= data_gb;
to_send <= not to_send;
wait for 2 ns;
acknow_wr <= '1';
wait until to_x01(wr_gb) = '0';
acknow_wr <= 'L';
end if;
end process;
-- process to detect write_to_scon operation
-- we still need to verify the timing of the execution of the instruction
PROCESS BEGIN
IF tb8_set = '1' THEN
scon(3) <= '1';
END IF;
IF tb8_reset = '1' THEN
scon(3) <= '0';
END IF;
IF rb8_set = '1' THEN
scon(2) <= '1';
END IF;
IF rb8_reset = '1' THEN
scon(2) <= '1';
END IF;
IF ti_set = '1' THEN
scon(1) <= '1';
END IF;
If ri_set = '1' THEN
scon(0) <= '1';
END IF;
IF wr_gb = '1' THEN
IF addr_gb = scon_addr and indirect_sel = '0' THEN -- CPU writes to serial port
scon <= data_gb;
wait for 2 ns;
acknow_wr <= '1';
wait until to_x01(wr_gb) = '0';
acknow_wr <= 'L';
END IF;
END IF;
WAIT ON tb8_set, tb8_reset, rb8_set, rb8_reset, ti_set, ri_set, wr_gb;
--WAIT ON ti_set, ri_set, wr_gb;
END PROCESS;
-- we assume that the write_to_sbuf pulse always occurs in s6p2
-- process to align to_send with s6p2 and convert it to a pulse
process begin
wait on to_send;
wait until cycle_state = s6p2;
write_to_sbuf <= '1';
-- wait for a cycle state
wait on cycle_state;
write_to_sbuf <= '0';
end process;
-- process to detect read_from_sbuf operation
-- we still need to verify the timing of the execution of the instruction
process begin
wait until rd_gb = '1';
if addr_gb = sbuf_addr and indirect_sel = '0' then -- CPU reads from serial port
data_gb <= sbuf_rd;
acknow_rd <= '1';
wait until rd_gb = '0';
data_gb <= hi_imp;
acknow_rd <= 'L';
end if;
end process;
-- process to detect read_from_scon operation
-- we still need to verify the timing of the execution of the instruction
process begin
wait until rd_gb = '1';
if addr_gb = scon_addr and indirect_sel = '0' then -- CPU reads from serial control
data_gb <= scon;
acknow_rd <= '1';
wait until rd_gb = '0';
data_gb <= hi_imp;
acknow_rd <= 'L';
end if;
end process;
-- process to resolve acknow_wr and acknow_rd to get acknow
acknow <= acknow_wr or acknow_rd;
-- transmitter process
-- initiated by rising edge of write_to_sbuf generated when CPU writes to sbuf
process (sm_r(2 downto 1), write_to_sbuf, cycle_state, tf1_16, p2clk_16)
variable i : integer := 0; -- iteration counter
variable sbuf_dup : byte := all_0; -- internal buffer for sbuf
variable to_send, no_start : bit := '0';
begin
-- if sm_r(2 downto 1) is changed, clear the current process
if sm_r(2 downto 1)'event then to_send := '0'; end if;
if rising_edge(write_to_sbuf) then -- initiate transmission
to_send := '1'; -- this signal means transmission in progress
sbuf_dup := sbuf_wr; -- fill internal buffer
i := 0; -- clear iteration counter
no_start := '1'; -- still in the write_to_sbuf cycle, don't start
ti_set <= '0'; -- lower the ti_set signal to create an edge later
end if;
if to_send = '1' then
-- sm_r is reversed sm
-- so mode 1 is "10", mode 2 is "01" OR IS IT? -MRM
case sm_r(2 downto 1) is
when "00" =>
-- mode 0, 8-bit shift register, Fxtal1/12 (one bit every machine cycle)
-- shift clock is output through txd
-- shift clock is low during s3, s4, s5, high during s6, s1, s2
-- actual data is output through rxd
if cycle_state'event then
case cycle_state is
when s1p1 => if i = 0 then no_start := '0'; -- start iteration
elsif i = 9 then -- time to stop
rxd <= 'H';
ti_set <= '1';
to_send := '0';
end if;
when s3p1 => if i /= 0 then txd_wr <= '0'; end if;
when s6p1 => if i /= 0 then txd_wr <= '1'; end if;
when s6p2 => if no_start = '0' then
if i < 8 then rxd <= sbuf_dup(i); end if;
i := i + 1;
end if;
when others => null;
end case;
end if;
when "01" =>
-- mode 1, 8-bit UART, baud rate set by timer 1
-- assumes that shift happens at the rollovers
if tf1_16'event and tf1_16 = 0 then
case i is
when 0 => txd_wr <= '0'; -- start bit
when 9 => txd_wr <= '1'; -- 9th bit ('1')
-- not sure when ti goes high, 9 or 10
ti_set <= '1';
when 10 => null; -- stop bit, keep high
when 11 => to_send := '0'; -- time to stop
when others => txd_wr <= sbuf_dup(i-1); -- shift the byte out
end case;
i := i + 1;
end if;
when "10" =>
-- mode 2, 9-bit UART, Fxtal1/64 or Fxtal1/32
-- assumes that shift happens at the rollovers
if p2clk_16'event and p2clk_16 = 0 then
case i is
when 0 => txd_wr <= '0'; -- start bit
when 9 => txd_wr <= tb8; -- 9th bit
when 10 => txd_wr <= '1'; -- stop bit
ti_set <= '1';
when 11 => to_send := '0'; -- time to stop
when others => txd_wr <= sbuf_dup(i-1); -- shift the byte out
end case;
i := i+1;
end if;
when "11" =>
-- mode 3, 9-bit UART, baud rate set by timer 1
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -