クロック同期回路のためのテストベンチ / UART RXモジュール / リニアテストベンチ
pp.542-544
以降シミュレーションだけなのでDUVのコードはテキストとまったく同じものを使う。ここではデータを1バイトだけDUVに与え、正しく受信できたことを確かめる。
↓ これがDUV。
library ieee; use ieee.std_logic_1164.all; use ieee.math_real.all; use ieee.numeric_std.all; entity uart_rx is generic ( -- 2400 -> 2604 -- 9600 -> 651 -- 115200 -> 54 -- 1562500 -> 4 -- 2083333 -> 3 DIVISOR: natural := 54 -- DIVISOR = 100,000,000 / (16 x BAUD_RATE) ); 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 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 rxd = '0' 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 4.34 us; rx_data <= transport rx_state.bits after 4.34 us; 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;
↓ これがテストベンチ。
library ieee; use ieee.std_logic_1164.all; entity uart_rx_tb is end; architecture testbench of uart_rx_tb is signal clock: std_logic := '0'; -- デフォルトの初期値は'U'であり、そのnotも'U'であるため、初期化しないとトグルできない。 signal reset: std_logic := '0'; signal rxd: std_logic := '1'; signal rx_strobe: std_logic; signal rx_data: std_logic_vector(7 downto 0); constant BIT_LENGTH: time := 8.68 us; -- ボーレート115200の1周期 begin duv: entity work.uart_rx port map ( clock => clock, reset => reset, rx_data => rx_data, rx_strobe => rx_strobe, rxd => rxd ); clock <= not clock after 5 ns; -- 100 MHz clockを生成する。 reset <= '1', '0' after 20 ns; stimuli_and_checker: process -- Start bit, data bits (LSB to MSB), and stop bit -- スタートビット0、LSBから順番に8ビット、最後にストップビット1、をテストデータとする。 constant TX_DATA: std_logic_vector := b"0_11001010_1"; begin wait until not reset; -- リセットが解除されるのを待って、 -- wait until rising_edge(clock) and reset = '0'; -- 処理の開始をクロックに同期させたい場合。 -- Drive Tx bits one at a time on DUV rxd line -- テストデータのスタートビットから順番に1ビットずつDUVのrxd信号に乗せる。 for i in TX_DATA'range loop rxd <= TX_DATA(i); wait for BIT_LENGTH; end loop; -- Wait for DUV to acknowledge the reception wait until rx_strobe for BIT_LENGTH; assert rx_strobe /= '1' report "rx_strobe is '1" severity note; assert rx_data /= "01010011" report "rx_data is 01010011" severity note; report "End of testbench. All tests passed."; std.env.finish; end process; end;
↓ 実行結果。LSBから1ビットずつ順番に受信してMSBに格納し、そのたびに全体を右にシフトする。