クロック同期回路のためのテストベンチ / UART RXモジュール / DUVの動作を実機で確認
pp.541-542
―――――――――――――――――――――――――――――――――――――
2018年6月に3万円弱で買ったロジックアナライザが2022年8月3日時点で7万円を超えている。
2018年6月:
2022年8月:
―――――――――――――――――――――――――――――――――――――
今度はUART RXモジュールを題材に順序論理回路のテストベンチを学ぶ。まずはDUVとして使うUART RXモジュールの動作を実機で確かめておく。クロック周波数を48 MHzに、ボーレートを9600に変えた以外はテキストのままである。
↓ 今'a' (= 0x61 = 0b0110_0001)を受信し終えたところである。黄色の線のタイミングでrx_strobe
がHiになる。
library ieee; use ieee.std_logic_1164.all; use ieee.math_real.all; use ieee.numeric_std.all; entity uart_rx is generic ( F_CPU: natural := 48_000_000; BAUD_RATE: natural := 9_600 ); port ( clock: in std_logic; reset: in std_logic; -- Client interface rx_data: out std_logic_vector(7 downto 0); -- received byte rx_strobe: out std_logic; -- validates received byte (1 system clock spike) -- Physical interface rxd: in std_logic ); end uart_rx; architecture Behavioral of uart_rx is type fsm_state_t is (idle, active); -- common to both RX and TX FSM type rx_state_t is record fsm_state: fsm_state_t; -- FSM state counter: unsigned(3 downto 0); -- tick count bits: std_logic_vector(7 downto 0); -- last 8 received bits nbits: unsigned(3 downto 0); -- number of received bits (includes start bit) enable: std_logic; -- signal we received a new byte end record; signal rx_state, rx_state_next: rx_state_t; constant DIVISOR: natural := F_CPU / (16 * BAUD_RATE); constant COUNTER_BITS: natural := integer(ceil(log2(real(DIVISOR)))); signal sample: std_logic; -- 1 clk spike at 16x baud rate signal sample_counter: unsigned(COUNTER_BITS - 1 downto 0); -- should fit values in 0..DIVISOR-1 begin -- RX state registers update at each CLK, and RESET reg_process: process (clock, reset) is begin if reset then rx_state.fsm_state <= idle; rx_state.bits <= (others => '0'); rx_state.nbits <= (others => '0'); rx_state.enable <= '0'; elsif rising_edge(clock) then rx_state <= rx_state_next; end if; end process; -- RX FSM: updates rx_state_next from rx_state and inputs. rx_process: process (rx_state, sample, rxd) is begin case rx_state.fsm_state is when idle => rx_state_next.counter <= (others => '0'); rx_state_next.bits <= (others => '0'); rx_state_next.nbits <= (others => '0'); rx_state_next.enable <= '0'; if not rxd then -- start a new byte rx_state_next.fsm_state <= active; else -- keep idle rx_state_next.fsm_state <= idle; end if; when active => rx_state_next <= rx_state; if sample then if rx_state.counter = x"8" then -- sample next RX bit (at the middle of the counter cycle) if rx_state.nbits = x"9" then rx_state_next.fsm_state <= idle; -- back to idle state to wait for next start bit rx_state_next.enable <= rxd; -- OK if stop bit is '1' else rx_state_next.bits <= rxd & rx_state.bits(7 downto 1); -- shift new bit in bits rx_state_next.nbits <= rx_state.nbits + 1; end if; end if; rx_state_next.counter <= rx_state.counter + 1; end if; end case; end process; -- RX output rx_output: process (rx_state) is begin rx_strobe <= transport rx_state.enable after 52.1 us; -- 1/(BAUD_RATE * 2) rx_data <= transport rx_state.bits after 52.1 us; -- 1/(BAUD_RATE * 2) end process; -- sample signal at 16x baud rate, 1 CLK spikes sample_process: process (clock, reset) is begin if reset then sample_counter <= (others => '0'); sample <= '0'; elsif rising_edge(clock) then if sample_counter = DIVISOR - 1 then sample <= '1'; sample_counter <= (others => '0'); else sample <= '0'; sample_counter <= sample_counter + 1; end if; end if; end process; end Behavioral;