クロック同期回路のためのテストベンチ / 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;