非同期シリアルインターフェイス(UART)で送信できるようにする

Interface (インターフェース) 2009年 09月号 [雑誌], pp.116-118

コードは下の2点を除いてほぼテキストのままである。

↓ これがシミュレーション結果(dinの8ビット値がストップビットに続いてlsbファーストでdoutから出力されている):
f:id:ti-nspire:20211004084745p:plain

↓ これが本体:

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity serial_tx is
    generic(
        F_CLK: positive := 48_000_000;
        BAUD : positive := 9600
    );
    port(
        clk   : in std_logic;
        aclr_n: in std_logic;
        wr    : in std_logic;
        din   : in std_logic_vector(7 downto 0);

        pol_inv: in std_logic; -- 0: 正論理; 1: 負論理    

        dout : out std_logic;
        ready: out std_logic
    );
end entity;

architecture rtl of serial_tx is
    signal in_din: std_logic_vector(7 downto 0) := (others => '0'); 
    signal buf   : std_logic_vector(7 downto 0) := (others => '0');

    signal serial_clk: std_logic := '0';

    signal load: std_logic := '0';
    signal run : std_logic := '0';

    signal cbit : natural range 0 to 7 :=  0 ;
    signal state: natural range 0 to 2 :=  0 ;
    
    signal dout_temp: std_logic := '0';

begin
    module_clk_gen: entity work.clk_gen
    generic map(
        F_CLK    => F_CLK,
        OUT_FREQ => BAUD
    )
    port map(
        aclr_n  => aclr_n,
        clk     => clk,
        clk_out => serial_clk
    );
    
    ready <=
        '1' when run = '0' and load = '0' else
        '0';
    
    dout <=
            dout_temp when pol_inv = '0' else
        not dout_temp;
    
    process(clk, aclr_n)
    begin
        if aclr_n = '0' then
            load <= '0';
        elsif rising_edge(clk) then
            if wr = '1' and run = '0' then 
                load   <= '1';
                in_din <= din;
            end if;
            if load = '1' and run = '1' then
                load <= '0';
            end if;
        end if;
    end process;
    
    process(serial_clk, aclr_n)
    begin
        if aclr_n = '0' then
            dout_temp <= '1';
            cbit      <=  0 ;
            state     <=  0 ;
            run       <= '0';
        elsif rising_edge(serial_clk) then
            case state is
                when 0 =>
                    cbit <= 0;
                    if load then
                        dout_temp <= '0'; --スタートビット
                        state     <= state + 1;
                        buf       <= in_din;
                        run <= '1';
                    else
                        dout_temp <= '1';
                        run <= '0';
                    end if;
                when 1 =>
                    if cbit <= 6 then
                        dout_temp <= buf(cbit); -- lsbから順番に送信する。
                        cbit      <= cbit + 1;
                    elsif cbit = 7 then
                        dout_temp <= buf(cbit);
                        state     <= state + 1;
                    end if;
                when 2 =>
                    dout_temp <= '1'; --ストップビット
                    state     <=  0 ;
                when others =>
                    state <= 0;
            end case;
        end if;
    end process;
    
end architecture;

↓ これがシリアル通信クロック生成器(テキストは分周器にしてあるがここでは分周比ではなく出力周波数で指定できるようにした):

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity clk_gen is
    generic(
        F_CLK   : positive := 48_000_000;
        OUT_FREQ: natural := 9600
       --OUT_FREQ: positive := 9600 * 16
        );
    port(
        aclr_n : in  std_logic;
        clk    : in  std_logic;
        clk_out: out std_logic := '0'
    );
end entity;

architecture rtl of clk_gen is
begin

    process(aclr_n, clk)
        constant TOP_VAL: natural                    := F_CLK/(OUT_FREQ * 2) - 1;
        variable count  : natural range 0 to TOP_VAL := 0;
    begin
        if aclr_n = '0' then
            count := 0;
        elsif rising_edge(clk) then
            if count >= TOP_VAL then
                clk_out <= not clk_out;
                count := 0;
            else
                count := count + 1;
            end if;
        end if;
    end process;

end architecture;

↓ これがテストベンチ:

-- Copyright (C) 2020  Intel Corporation. All rights reserved.
-- Your use of Intel Corporation's design tools, logic functions 
-- and other software and tools, and any partner logic 
-- functions, and any output files from any of the foregoing 
-- (including device programming or simulation files), and any 
-- associated documentation or information are expressly subject 
-- to the terms and conditions of the Intel Program License 
-- Subscription Agreement, the Intel Quartus Prime License Agreement,
-- the Intel FPGA IP License Agreement, or other applicable license
-- agreement, including, without limitation, that your use is for
-- the sole purpose of programming logic devices manufactured by
-- Intel and sold by Intel or its authorized distributors.  Please
-- refer to the applicable agreement for further details, at
-- https://fpgasoftware.intel.com/eula.

-- ***************************************************************************
-- This file contains a Vhdl test bench template that is freely editable to   
-- suit user's needs .Comments are provided in each section to help the user  
-- fill out necessary details.                                                
-- ***************************************************************************
-- Generated on "10/02/2021 15:20:16"
                                                            
-- Vhdl Test Bench template for design  :  serial_tx
-- 
-- Simulation tool : ModelSim-Altera (VHDL)
-- 

LIBRARY ieee;                                               
USE ieee.std_logic_1164.all;                                

ENTITY serial_tx_vhd_tst IS
END serial_tx_vhd_tst;
ARCHITECTURE serial_tx_arch OF serial_tx_vhd_tst IS
-- constants                                                 
-- signals                                                   
SIGNAL aclr_n : STD_LOGIC;
SIGNAL clk : STD_LOGIC;
SIGNAL din : STD_LOGIC_VECTOR(7 DOWNTO 0);
SIGNAL dout : STD_LOGIC;
SIGNAL pol_inv : STD_LOGIC;
SIGNAL ready : STD_LOGIC;
SIGNAL wr : STD_LOGIC;
COMPONENT serial_tx
    PORT (
    aclr_n : IN STD_LOGIC;
    clk : IN STD_LOGIC;
    din : IN STD_LOGIC_VECTOR(7 DOWNTO 0);
    dout : OUT STD_LOGIC;
    pol_inv : IN STD_LOGIC;
    ready : OUT STD_LOGIC;
    wr : IN STD_LOGIC
    );
END COMPONENT;
BEGIN
    i1 : serial_tx
    PORT MAP (
-- list connections between master ports and signals
    aclr_n => aclr_n,
    clk => clk,
    din => din,
    dout => dout,
    pol_inv => pol_inv,
    ready => ready,
    wr => wr
    );

process
begin
    aclr_n  <= '1';
    pol_inv <= '0';
    wait;
end process;

process
begin
    clk <= '1'; wait for 10417 ps;
    clk <= '0'; wait for 10417 ps;
end process;

process
begin
    wr <= '0'; wait for 100 us;
    wr <= '1'; din <= 8b"1011_0101"; wait for 100 ns;
    wr <= '0'; wait for 1200 us;
    wr <= '1'; din <= 8b"1011_0111"; wait for 100 ns;

    wr <= '0'; wait;
end process;


    
END serial_tx_arch;