📄 uart.vhd
字号:
-- 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 <= 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 others => null;
end case;
end if;
end process;
-- process to receive data from outside
-- reception start conditions:
-- for mode 0 the condition is ri = '0' and ren = '1'
-- for other modes the condition is ren = '1' and falling edge of rxd
process (sm_r(2 downto 1), cycle_state, rf1_16, p2clk_16, rxd)
variable i : integer := 0; -- iteration counter
variable samp1, samp2, samp3, rcvd_bit : std_logic;
variable sbuf_dup : byte; -- internal buffer for sbuf
-- indicate whether reception is in session
variable rcv_in_session : std_logic := '0';
begin
-- if sm_r(2 downto 1) is changed, clear the current process
if sm_r(2 downto 1)'event then rcv_in_session := '0'; end if;
case sm_r(2 downto 1) is
when "00" =>
-- mode 0, 8-bit shift register, Fxtal1/12
-- shift clock is output through txd
-- shift clock is low during s3, s4, s5, high during s6, s1, s2
-- actual data is sampled in through rxd during s5p2
if cycle_state'event and ri = '0' and ren = '1' then
if rcv_in_session = '0' then -- initiate reception
rcv_in_session := '1';
i := 0;
ri_set <= '0'; -- lower ri_set signal to create an edge later
else -- already in reception
case cycle_state is
when s1p1 => if i = 9 then -- time to stop
sbuf_rd <= sbuf_dup;
ri_set <= '1'; -- set ri
rcv_in_session := '0';
end if;
when s3p1 => if i /= 0 then txd_rd <= '0'; end if;
-- assume that ri is cleared after s5p2 in the cycle
when s5p2 => if i /= 0 then sbuf_dup(i-1) := rxd; end if;
i := i + 1;
when s6p1 => if i /= 0 then txd_rd <= '1'; end if;
when others => null;
end case;
end if;
end if;
when "01" =>
-- mode 1, 8-bit UART, baud rate set by timer 1
-- we can change this falling edge detection into sampling
if falling_edge(rxd) and (ren = '1') and (rcv_in_session = '0') then
rcv_in_session := '1';
rf1_16_reset <= '1'; -- reset the counter immediately
i := 0;
rb8_set <= '0';
rb8_reset <= '0';
ri_set <= '0';
end if;
if rf1_16'event and (rcv_in_session = '1') then
case rf1_16 is
when 7 => samp1 := rxd; -- first sample
when 8 => samp2 := rxd; -- second sample
when 9 =>
samp3 := rxd; -- third sample
-- take the value which appears at least twice, for noise rejection
if samp1 = samp2 or samp2 = samp3 then rcvd_bit := samp2;
else rcvd_bit := samp1;
end if;
if i = 0 then -- start bit
if rcvd_bit /= '0' then -- false start bit, start over
rcv_in_session := '0';
rf1_16_reset <= '0';
end if;
elsif i = 9 then -- stop bit
-- two conditions to meet for successful reception completion
if ri = '0' and (sm_r(0) = '0' or rcvd_bit = '1') then
sbuf_rd <= sbuf_dup;
-- save rcvd_bit in rb8
if rcvd_bit = '1' then rb8_set <= '1';
else rb8_reset <= '1';
end if;
ri_set <= '1';
end if;
rcv_in_session := '0';
rf1_16_reset <= '0';
else sbuf_dup(i-1) := rcvd_bit; -- data bits
end if;
i := i + 1;
when others => null;
end case;
end if;
when "10" =>
-- mode 2, 9-bit UART, Fxtal1/64 or Fxtal1/32
-- we can change this falling edge detection into sampling
if falling_edge(rxd) and (ren = '1') and (rcv_in_session = '0') then
rcv_in_session := '1';
p2clk_16_reset <= '1'; -- reset the counter
i := 0;
rb8_set <= '0';
rb8_reset <= '0';
ri_set <= '0';
end if;
if p2clk_16'event and (rcv_in_session = '1') then
case p2clk_16 is
when 7 => samp1 := rxd; -- first sample
when 8 => samp2 := rxd; -- second sample
when 9 =>
samp3 := rxd; -- third sample
-- take the value which appears at least twice, for noise rejection
if samp1 = samp2 or samp2 = samp3 then rcvd_bit := samp2;
else rcvd_bit := samp1;
end if;
if i = 0 then -- start bit
if rcvd_bit /= '0' then -- false start bit, start over
rcv_in_session := '0';
p2clk_16_reset <= '0';
end if;
elsif i = 9 then -- stop bit
-- two conditions to meet for successful reception completion
if ri = '0' and (sm_r(0) = '0' or rcvd_bit = '1') then
sbuf_rd <= sbuf_dup;
-- save rcvd_bit in rb8
if rcvd_bit = '1' then rb8_set <= '1';
else rb8_reset <= '1';
end if;
ri_set <= '1';
end if;
rcv_in_session := '0';
p2clk_16_reset <= '0';
else sbuf_dup(i-1) := rcvd_bit; -- data bits
end if;
i := i + 1;
when others => null;
end case;
end if;
when "11" =>
-- mode 3, 9-bit UART, baud rate set by timer 1
-- we can change this falling edge detection into sampling
if falling_edge(rxd) and (ren = '1') and (rcv_in_session = '0') then
rcv_in_session := '1';
rf1_16_reset <= '1'; -- reset the counter
i := 0;
rb8_set <= '0';
rb8_reset <= '0';
ri_set <= '0';
end if;
if rf1_16'event and (rcv_in_session = '1') then
case rf1_16 is
when 7 => samp1 := rxd; -- first sample
when 8 => samp2 := rxd; -- second sample
when 9 =>
samp3 := rxd; -- third sample
-- take the value which appears at least twice, for noise rejection
if samp1 = samp2 or samp2 = samp3 then rcvd_bit := samp2;
else rcvd_bit := samp1;
end if;
if i = 0 then -- start bit
if rcvd_bit /= '0' then -- false start bit, start over
rcv_in_session := '0';
rf1_16_reset <= '0';
end if;
elsif i = 9 then -- stop bit
-- two conditions to meet for successful reception completion
if ri = '0' and (sm_r(0) = '0' or rcvd_bit = '1') then
sbuf_rd <= sbuf_dup;
-- save rcvd_bit in rb8
if rcvd_bit = '1' then rb8_set <= '1';
else rb8_reset <= '1';
end if;
ri_set <= '1';
end if;
rcv_in_session := '0';
rf1_16_reset <= '0';
else sbuf_dup(i-1) := rcvd_bit; -- data bits
end if;
i := i + 1;
when others => null;
end case;
end if;
when others => null;
end case;
end process;
-- the rxd signal has got two drivers: outside and transmitter
-- in mode 0 both can drive rxd
-- in other modes only outside drives rxd
-- when transmitter is not driving it outputs 'H'
-- process to resolve the txd signal
-- it's got two drivers: transmitter and receiver
-- in mode 0 both transmitter and receiver can drive txd
-- in other modes only transmitter drives txd
-- when one is not driving txd, it outputs '1'
txd <= txd_wr and txd_rd;
-- process to generate the divide-by-16 counter of timer 1 overflow signal
-- the clock of this counter is 16 times slower than the timer 1 overflow signal
-- we don't know in which cycle_state tf1 will be set
-- so have to synchronize it with s1p1
process (tf1, cycle_state, tf1_16_reset, rf1_16_reset)
variable tf1_flag : bit := '0';
variable tf1_half : bit := '0';
begin
-- rising edge of tf1_16_reset clears the counter
-- could be level-triggered
if rising_edge(tf1_16_reset) then tf1_16 <= 0; end if;
if rising_edge(rf1_16_reset) then rf1_16 <= 0; end if;
--if rising_edge(tf1) then tf1_flag := '1'; end if;
--if cycle_state'event and cycle_state = s1p1 and tf1_flag = '1' then
if rising_edge(tf1) then
if smod = '0' then -- cut the frequency in half
tf1_half := not tf1_half;
end if;
if not (smod = '0' and tf1_half = '1') then -- increment the counter
tf1_16 <= (tf1_16 + 1) mod 16;
rf1_16 <= (rf1_16 + 1) mod 16;
--tf1_flag := '0';
end if;
end if;
end process;
-- process to generate the divide-by-16 counter of the phase 2 clock
-- phase 2 clock is twice as slow as the xtal1 clock
process (p2clk, p2clk_16_reset)
variable p2clk_half : bit := '0';
begin
-- rising edge of p2clk_16_reset clears the counter
-- could be level-triggered
if rising_edge(p2clk_16_reset) then p2clk_16 <= 0; end if;
-- it could be rising edge depending on how it aligns with cycle_state
if falling_edge(p2clk) then
if smod = '0' then -- cut the frequency in half
p2clk_half := not p2clk_half;
end if;
if not (smod = '0' and p2clk_half = '1') then -- increment the counter
p2clk_16 <= (p2clk_16 + 1) mod 16;
end if;
end if;
end process;
end behave;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -