diff --git a/lib/rtl_components.vhd b/lib/rtl_components.vhd index 10e614a..5330a7b 100644 --- a/lib/rtl_components.vhd +++ b/lib/rtl_components.vhd @@ -129,30 +129,23 @@ package components is component CC_FIFO_40K generic ( - -- Location format: D(0..N-1)X(0..3)Y(0..7) or UNPLACED - LOC : string := "UNPLACED"; - -- Offset configuration - ALMOST_FULL_OFFSET : std_logic_vector (12 downto 0) := (others => '0'); - ALMOST_EMPTY_OFFSET : std_logic_vector (12 downto 0) := (others => '0'); - -- Port Widths - A_WIDTH : natural := 0; - B_WIDTH : natural := 0; - -- RAM and Write Modes - RAM_MODE : string := "SDP"; -- "TPD" or "SDP" - FIFO_MODE : string := "SYNC"; -- "ASYNC" or "SYNC" - -- Inverting Control Pins - A_CLK_INV : std_logic := '0'; - B_CLK_INV : std_logic := '0'; - A_EN_INV : std_logic := '0'; - B_EN_INV : std_logic := '0'; - A_WE_INV : std_logic := '0'; - B_WE_INV : std_logic := '0'; - -- Output Register - A_DO_REG : std_logic := '0'; - B_DO_REG : std_logic := '0'; - -- Error Checking and Correction - A_ECC_EN : std_logic := '0'; - B_ECC_EN : std_logic := '0' + LOC : string := "UNPLACED"; -- Location format: D(0..N-1)X(0..3)Y(0..7) or UNPLACED + ALMOST_FULL_OFFSET : std_logic_vector (12 downto 0) := (others => '0'); -- Almost full offset + ALMOST_EMPTY_OFFSET : std_logic_vector (12 downto 0) := (others => '0'); -- Almost empty offset + A_WIDTH : natural := 0; -- Port A Width + B_WIDTH : natural := 0; -- Port B Width + RAM_MODE : string := "SDP"; -- RAM mode: "TPD" or "SDP" + FIFO_MODE : string := "SYNC"; -- Write mode: "ASYNC" or "SYNC" + A_CLK_INV : std_logic := '0'; -- Inverting Control Pins + B_CLK_INV : std_logic := '0'; -- Inverting Control Pins + A_EN_INV : std_logic := '0'; -- Inverting Control Pins + B_EN_INV : std_logic := '0'; -- Inverting Control Pins + A_WE_INV : std_logic := '0'; -- Inverting Control Pins + B_WE_INV : std_logic := '0'; -- Inverting Control Pins + A_DO_REG : std_logic := '0'; -- Port A Output Register + B_DO_REG : std_logic := '0'; -- Port B Output Register + A_ECC_EN : std_logic := '0'; -- Port A Error Checking and Correction + B_ECC_EN : std_logic := '0' -- Port B Error Checking and Correction ); port ( A_ECC_1B_ERR : out std_logic; @@ -179,14 +172,14 @@ package components is F_ALMOST_FULL_OFFSET : in std_logic_vector(12 downto 0); F_ALMOST_EMPTY_OFFSET : in std_logic_vector(12 downto 0); -- FIFO status signals - F_FULL : out std_logic; - F_EMPTY : out std_logic; - F_ALMOST_FULL : out std_logic; - F_ALMOST_EMPTY: out std_logic; - F_RD_ERROR : out std_logic; - F_WR_ERROR : out std_logic; - F_RD_PTR : out std_logic_vector(15 downto 0); - F_WR_PTR : out std_logic_vector(15 downto 0) + F_FULL : out std_logic; + F_EMPTY : out std_logic; + F_ALMOST_FULL : out std_logic; + F_ALMOST_EMPTY : out std_logic; + F_RD_ERROR : out std_logic; + F_WR_ERROR : out std_logic; + F_RD_PTR : out std_logic_vector(15 downto 0); + F_WR_PTR : out std_logic_vector(15 downto 0) ); end component; @@ -196,5 +189,12 @@ package components is ); end component; + component CC_BUFG + port ( + I : in std_logic; + O : out std_logic + ); + end component; + end package components; \ No newline at end of file diff --git a/lib/user_components.vhd b/lib/user_components.vhd index 0f31cda..8de81a1 100644 --- a/lib/user_components.vhd +++ b/lib/user_components.vhd @@ -1,13 +1,14 @@ -library ieee ; -use ieee.std_logic_1164.all; - - -- Async reset synchronizer circuit inspired from -- Chris Cummings SNUG 2002 paper -- Synchronous Resets? Asynchronous Resets? -- I am so confused! -- How will I ever know which to use? + +library ieee ; +use ieee.std_logic_1164.all; + + entity reset_sync is generic ( POLARITY : std_logic := '0' @@ -38,3 +39,384 @@ begin rst_o <= s_rst_d(1); end architecture; + + +-- Async reset synchronizer circuit inspired from +-- Chris Cummings SNUG 2002 paper +-- Synchronous Resets? Asynchronous Resets? +-- I am so confused! +-- How will I ever know which to use? + + +library ieee ; +use ieee.std_logic_1164.all; + + +entity reset_sync_slv is +generic ( + POLARITY : std_logic := '0' +); +port ( + clk_i : in std_logic; + rst_i : in std_logic_vector; + rst_o : out std_logic_vector +); +end entity; + + +architecture sim of reset_sync_slv is + +begin + + GEN : for i in rst_i'range generate + signal s_rst_d : std_logic_vector(1 downto 0); + begin + + process (clk_i, rst_i(i)) is + begin + if (rst_i(i) = POLARITY) then + s_rst_d <= (others => POLARITY); + elsif (rising_edge(clk_i)) then + s_rst_d <= s_rst_d(0) & not POLARITY; + end if; + end process; + + rst_o(i) <= s_rst_d(1); + + end generate; + +end architecture; + + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + + +entity fifo is + generic ( + DEPTH : positive := 16; + WIDTH : positive := 16 + ); + port ( + rst_n_i : in std_logic; + clk_i : in std_logic; + -- write + wen_i : in std_logic; + din_i : in std_logic_vector(WIDTH-1 downto 0); + full_o : out std_logic; + werror_o : out std_logic; + -- read + ren_i : in std_logic; + dout_o : out std_logic_vector(WIDTH-1 downto 0); + empty_o : out std_logic; + rerror_o : out std_logic + ); +end entity fifo; + + +architecture rtl of fifo is + + subtype t_fifo_pnt is natural range 0 to DEPTH-1; + signal s_write_pnt : t_fifo_pnt; + signal s_read_pnt : t_fifo_pnt; + + type t_fifo_mem is array (t_fifo_pnt'low to t_fifo_pnt'high) of std_logic_vector(din_i'range); + signal s_fifo_mem : t_fifo_mem; + + signal s_almost_full : boolean; + signal s_almost_empty : boolean; + + function incr_pnt (data : t_fifo_pnt) return t_fifo_pnt is + begin + if (data = t_fifo_mem'high) then + return 0; + end if; + return data + 1; + end function incr_pnt; + +begin + + s_almost_full <= (s_write_pnt = s_read_pnt - 1) or + (s_write_pnt = t_fifo_mem'high and s_read_pnt = t_fifo_mem'low); + + s_almost_empty <= (s_read_pnt = s_write_pnt - 1) or + (s_read_pnt = t_fifo_mem'high and s_write_pnt = t_fifo_mem'low); + + WriteP : process (rst_n_i, clk_i) is + begin + if (not rst_n_i) then + s_write_pnt <= 0; + werror_o <= '0'; + elsif (rising_edge(clk_i)) then + werror_o <= Wen_i and Full_o; + if (Wen_i = '1' and Full_o = '0') then + s_fifo_mem(s_write_pnt) <= Din_i; + s_write_pnt <= incr_pnt(s_write_pnt); + end if; + end if; + end process WriteP; + + ReadP : process (rst_n_i, clk_i) is + begin + if (not rst_n_i) then + s_read_pnt <= 0; + rerror_o <= '0'; + elsif (rising_edge(clk_i)) then + rerror_o <= Ren_i and Empty_o; + if (Ren_i = '1' and Empty_o = '0') then + Dout_o <= s_fifo_mem(s_read_pnt); + s_read_pnt <= incr_pnt(s_read_pnt); + end if; + end if; + end process ReadP; + + FlagsP : process (rst_n_i, clk_i) is + begin + if (rst_n_i = '0') then + Full_o <= '0'; + Empty_o <= '1'; + elsif (rising_edge(clk_i)) then + if (Wen_i = '1') then + if (Ren_i = '0' and s_almost_full) then + Full_o <= '1'; + end if; + Empty_o <= '0'; + end if; + if (Ren_i = '1') then + if (Wen_i = '0' and s_almost_empty) then + Empty_o <= '1'; + end if; + Full_o <= '0'; + end if; + end if; + end process FlagsP; + +end architecture; + + +-- Synchronous AXI stream FIFO based on generic fifo +-- component. Configurable depth and width. + + +library ieee ; +use ieee.std_logic_1164.all; + +library gatemate; +use gatemate.components.all; + + +entity axis_fifo is +generic ( + DEPTH : positive := 8; + WIDTH : positive := 8 +); +port ( + -- global + rst_n_i : in std_logic; + clk_i : in std_logic; + -- axis in + tdata_i : in std_logic_vector(WIDTH-1 downto 0); + tvalid_i : in std_logic; + tready_o : out std_logic; + -- axis aout + tdata_o : out std_logic_vector(WIDTH-1 downto 0); + tvalid_o : out std_logic; + tready_i : in std_logic +); +end entity; + + +architecture rtl of axis_fifo is + + signal s_fifo_wen : std_logic; + signal s_fifo_ren : std_logic; + signal s_fifo_empty : std_logic; + signal s_fifo_full : std_logic; + signal s_fwft_empty : std_logic; + signal s_ren : std_logic; + +begin + + fifo : entity work.fifo + generic map ( + DEPTH => DEPTH, + WIDTH => WIDTH + ) + port map ( + rst_n_i => rst_n_i, + clk_i => clk_i, + -- write + wen_i => s_fifo_wen, + din_i => tdata_i, + full_o => s_fifo_full, + werror_o => open, + -- read + ren_i => s_fifo_ren, + dout_o => tdata_o, + empty_o => s_fifo_empty, + rerror_o => open + ); + + -- FWFT logic + process (clk_i, rst_n_i) is + begin + if (not rst_n_i) then + s_fwft_empty <= '1'; + elsif (rising_edge(clk_i)) then + if (s_fifo_ren) then + s_fwft_empty <= '0'; + elsif (s_ren) then + s_fwft_empty <= '1'; + end if; + end if; + end process; + + s_fifo_ren <= not s_fifo_empty and (s_fwft_empty or s_ren); + + -- AXIS logic + s_fifo_wen <= tvalid_i and not s_fifo_full; + s_ren <= tready_i and not s_fwft_empty; + + tready_o <= not s_fifo_full; + + tvalid_o <= not s_fwft_empty; +end architecture; + + +-- Synchronous AXI stream FIFO based on GateMate CC_FIFO_40K +-- primitive + + +library ieee ; +use ieee.std_logic_1164.all; + +library gatemate; +use gatemate.components.all; + + +entity axis_fifo_gm is +generic ( + WIDTH : positive := 8 +); +port ( + -- global + rst_n_i : in std_logic; + clk_i : in std_logic; + -- axis in + tdata_i : in std_logic_vector(WIDTH-1 downto 0); + tvalid_i : in std_logic; + tready_o : out std_logic; + -- axis aout + tdata_o : out std_logic_vector(WIDTH-1 downto 0); + tvalid_o : out std_logic; + tready_i : in std_logic +); +end entity; + + +architecture rtl of axis_fifo_gm is + + signal s_fifo_wen : std_logic; + signal s_fifo_ren : std_logic; + signal s_fifo_empty : std_logic; + signal s_fifo_full : std_logic; + signal s_fwft_empty : std_logic; + signal s_ren : std_logic; + + signal s_fifo_a_en : std_logic; + signal s_fifo_b_en : std_logic; + signal s_fifo_b_we : std_logic; + + signal s_fifo_din : std_logic_vector(79 downto 0); + signal s_fifo_dout : std_logic_vector(79 downto 0); + +begin + + -- CC_FIFO_40K instance (512x80) + fifo : CC_FIFO_40K + generic map ( + LOC => "UNPLACED", + ALMOST_FULL_OFFSET => (others => '0'), + ALMOST_EMPTY_OFFSET => (others => '0'), + A_WIDTH => WIDTH, -- 1..80 + B_WIDTH => WIDTH, -- 1..80 + RAM_MODE => "SDP", + FIFO_MODE => "SYNC", + A_CLK_INV => '0', + B_CLK_INV => '0', + A_EN_INV => '0', + B_EN_INV => '0', + A_WE_INV => '0', + B_WE_INV => '0', + A_DO_REG => '0', + B_DO_REG => '0', + A_ECC_EN => '0', + B_ECC_EN => '0' + ) + port map( + A_ECC_1B_ERR => open, + B_ECC_1B_ERR => open, + A_ECC_2B_ERR => open, + B_ECC_2B_ERR => open, + -- FIFO pop port + A_DO => s_fifo_dout(39 downto 0), + B_DO => s_fifo_dout(79 downto 40), + + A_CLK => clk_i, + A_EN => s_fifo_a_en, + -- FIFO push port + A_DI => s_fifo_din(39 downto 0), + B_DI => s_fifo_din(79 downto 40), + A_BM => (others => '1'), + B_BM => (others => '1'), + + B_CLK => clk_i, + B_EN => s_fifo_b_en, + B_WE => s_fifo_b_we, + -- FIFO control + F_RST_N => rst_n_i, + F_ALMOST_FULL_OFFSET => (others => '0'), + F_ALMOST_EMPTY_OFFSET => (others => '0'), + -- FIFO status signals + F_FULL => s_fifo_full, + F_EMPTY => s_fifo_empty, + F_ALMOST_FULL => open, + F_ALMOST_EMPTY => open, + F_RD_ERROR => open, + F_WR_ERROR => open, + F_RD_PTR => open, + F_WR_PTR => open + ); + + s_fifo_b_en <= s_fifo_wen; + s_fifo_b_we <= s_fifo_wen; + s_fifo_a_en <= s_fifo_ren; + + -- FWFT logic + process (clk_i, rst_n_i) is + begin + if (not rst_n_i) then + s_fwft_empty <= '1'; + elsif (rising_edge(clk_i)) then + if (s_fifo_ren) then + s_fwft_empty <= '0'; + elsif (s_ren) then + s_fwft_empty <= '1'; + end if; + end if; + end process; + + s_fifo_ren <= not s_fifo_empty and (s_fwft_empty or s_ren); + + -- AXIS logic + s_fifo_wen <= tvalid_i and not s_fifo_full; + s_ren <= tready_i and not s_fwft_empty; + + tready_o <= not s_fifo_full; + s_fifo_din(tdata_i'range) <= tdata_i; + + tvalid_o <= not s_fwft_empty; + tdata_o <= s_fifo_dout(tdata_o'range); + +end architecture; diff --git a/uart_loop/rtl/uart_loop.vhd b/uart_loop/rtl/uart_loop.vhd new file mode 100644 index 0000000..e03c3c8 --- /dev/null +++ b/uart_loop/rtl/uart_loop.vhd @@ -0,0 +1,126 @@ +-- This design implements a register file which can +-- be accessed by an UART with 9600 baud +-- +-- See into uart_ctrl.vhd for documentation of the protocol +-- used to read / write the register file. + + +library ieee ; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library gatemate; +use gatemate.components.all; + + +entity uart_loop is +port ( + clk_i : in std_logic; -- 10 MHz clock + rst_n_i : in std_logic; -- SW3 button + uart_rx_i : in std_logic; -- PMODA IO3 + uart_tx_o : out std_logic -- PMODA IO5 +); +end entity uart_loop; + + +architecture rtl of uart_loop is + + signal s_pll_clk : std_logic; + signal s_pll_lock : std_logic; + + signal s_rst_n : std_logic; + signal s_cfg_end : std_logic; + + signal s_uart_rx_tdata : std_logic_vector(7 downto 0); + signal s_uart_rx_tvalid : std_logic; + signal s_uart_rx_tready : std_logic; + + signal s_uart_tx_tdata : std_logic_vector(7 downto 0); + signal s_uart_tx_tvalid : std_logic; + signal s_uart_tx_tready : std_logic; + +begin + + pll : CC_PLL + generic map ( + REF_CLK => "10", + OUT_CLK => "1", + PERF_MD => "ECONOMY" + ) + port map ( + CLK_REF => clk_i, + CLK_FEEDBACK => '0', + USR_CLK_REF => '0', + USR_LOCKED_STDY_RST => '0', + USR_PLL_LOCKED_STDY => open, + USR_PLL_LOCKED => s_pll_lock, + CLK270 => open, + CLK180 => open, + CLK0 => s_pll_clk, + CLK90 => open, + CLK_REF_OUT => open + ); + + cfg_end_inst : CC_CFG_END + port map ( + CFG_END => s_cfg_end + ); + + uart_rx : entity work.uart_rx + generic map ( + CLK_DIV => 104 + ) + port map ( + -- globals + rst_n_i => s_rst_n, + clk_i => s_pll_clk, + -- axis user interface + tdata_o => s_uart_rx_tdata, + tvalid_o => s_uart_rx_tvalid, + tready_i => s_uart_rx_tready, + -- uart interface + rx_i => uart_rx_i + ); + + axis_fifo : entity work.axis_fifo + generic map ( + DEPTH => 64, + WIDTH => 8 + ) + port map ( + -- globals + rst_n_i => s_rst_n, + clk_i => s_pll_clk, + -- uart rx interface + tdata_i => s_uart_rx_tdata, + tvalid_i => s_uart_rx_tvalid, + tready_o => s_uart_rx_tready, + -- uart tx interface + tdata_o => s_uart_tx_tdata, + tvalid_o => s_uart_tx_tvalid, + tready_i => s_uart_tx_tready + ); + +-- s_uart_tx_tdata <= s_uart_rx_tdata; +-- s_uart_tx_tvalid <= s_uart_rx_tvalid; +-- s_uart_rx_tready <= s_uart_tx_tready; + + uart_tx : entity work.uart_tx + generic map ( + CLK_DIV => 104 + ) + port map ( + -- globals + rst_n_i => s_rst_n, + clk_i => s_pll_clk, + -- axis user interface + tdata_i => s_uart_tx_tdata, + tvalid_i => s_uart_tx_tvalid, + tready_o => s_uart_tx_tready, + -- uart interface + tx_o => uart_tx_o + ); + + s_rst_n <= rst_n_i and s_pll_lock and s_cfg_end; + +end architecture; diff --git a/uart_loop/rtl/uart_rx.vhd b/uart_loop/rtl/uart_rx.vhd new file mode 100644 index 0000000..680e747 --- /dev/null +++ b/uart_loop/rtl/uart_rx.vhd @@ -0,0 +1,105 @@ +-- Copyright (c) 2022 by Torsten Meissner +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- https://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. + + + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + + +entity uart_rx is + generic ( + CLK_DIV : natural := 10 + ); + port ( + -- globals + rst_n_i : in std_logic; + clk_i : in std_logic; + -- axis user interface + tdata_o : out std_logic_vector(7 downto 0); + tvalid_o : out std_logic; + tready_i : in std_logic; + -- uart interface + rx_i : in std_logic + ); +end entity uart_rx; + + +architecture rtl of uart_rx is + + type t_uart_state is (IDLE, RECEIVE, VALID); + signal s_uart_state : t_uart_state; + + signal s_clk_en : std_logic; + signal s_clk_cnt : natural range 0 to CLK_DIV-1; + signal s_bit_cnt : natural range 0 to tdata_o'length+1; + signal s_rx_d : std_logic_vector(3 downto 0); + +begin + + ClkDivP : process (clk_i, rst_n_i) is + begin + if (not rst_n_i) then + s_clk_cnt <= CLK_DIV-1; + elsif (rising_edge(clk_i)) then + if (s_uart_state = IDLE) then + s_clk_cnt <= CLK_DIV-2; + elsif (s_uart_state = RECEIVE) then + if (s_clk_cnt = 0) then + s_clk_cnt <= CLK_DIV-1; + else + s_clk_cnt <= s_clk_cnt - 1; + end if; + end if; + end if; + end process ClkDivP; + + s_clk_en <= '1' when s_uart_state = RECEIVE and s_clk_cnt = CLK_DIV/2-1 else '0'; + + RxP : process (clk_i, rst_n_i) is + begin + if (not rst_n_i) then + s_uart_state <= IDLE; + tdata_o <= (others => '0'); + s_rx_d <= x"1"; + s_bit_cnt <= 0; + elsif (rising_edge(clk_i)) then + s_rx_d <= s_rx_d(2 downto 0) & rx_i; + FsmL : case s_uart_state is + when IDLE => + s_bit_cnt <= tdata_o'length+1; + if (s_rx_d = "1000") then + s_uart_state <= RECEIVE; + end if; + when RECEIVE => + if (s_clk_en) then + if (s_bit_cnt = 0) then + s_uart_state <= VALID; + else + tdata_o <= s_rx_d(3) & tdata_o(tdata_o'length-1 downto 1); + s_bit_cnt <= s_bit_cnt - 1; + end if; + end if; + when VALID => + if (tready_i) then + s_uart_state <= IDLE; + end if; + end case; + end if; + end process RxP; + + tvalid_o <= '1' when s_uart_state = VALID else '0'; + +end architecture rtl; diff --git a/uart_loop/rtl/uart_tx.vhd b/uart_loop/rtl/uart_tx.vhd new file mode 100644 index 0000000..5ce450c --- /dev/null +++ b/uart_loop/rtl/uart_tx.vhd @@ -0,0 +1,102 @@ +-- Copyright (c) 2022 by Torsten Meissner +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- https://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. + + + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + + +entity uart_tx is + generic ( + CLK_DIV : natural := 10 + ); + port ( + -- globals + rst_n_i : in std_logic; + clk_i : in std_logic; + -- axis user interface + tdata_i : in std_logic_vector(7 downto 0); + tvalid_i : in std_logic; + tready_o : out std_logic; + -- uart interface + tx_o : out std_logic + ); +end entity uart_tx; + + +architecture rtl of uart_tx is + + type t_uart_state is (IDLE, SEND); + signal s_uart_state : t_uart_state; + + signal s_data : std_logic_vector(tdata_i'length+1 downto 0); + signal s_clk_cnt : natural range 0 to CLK_DIV-1; + signal s_clk_en : std_logic; + signal s_bit_cnt : natural range 0 to s_data'length-1; + +begin + + ClkDivP : process (clk_i, rst_n_i) is + begin + if (not rst_n_i) then + s_clk_cnt <= CLK_DIV-1; + elsif (rising_edge(clk_i)) then + if (s_uart_state = IDLE) then + s_clk_cnt <= CLK_DIV-2; + elsif (s_uart_state = SEND) then + if (s_clk_cnt = 0) then + s_clk_cnt <= CLK_DIV-1; + else + s_clk_cnt <= s_clk_cnt - 1; + end if; + end if; + end if; + end process ClkDivP; + + s_clk_en <= '1' when s_uart_state = SEND and s_clk_cnt = 0 else '0'; + + TxP : process (clk_i, rst_n_i) is + begin + if (not rst_n_i) then + s_uart_state <= IDLE; + s_data <= (0 => '1', others => '0'); + s_bit_cnt <= 0; + elsif (rising_edge(clk_i)) then + FsmL : case s_uart_state is + when IDLE => + s_bit_cnt <= s_data'length-1; + if (tvalid_i) then + s_data <= '1' & tdata_i & '0'; + s_uart_state <= SEND; + end if; + when SEND => + if (s_clk_en) then + s_data <= '1' & s_data(s_data'length-1 downto 1); + if (s_bit_cnt = 0) then + s_uart_state <= IDLE; + else + s_bit_cnt <= s_bit_cnt - 1; + end if; + end if; + end case; + end if; + end process TxP; + + tready_o <= '1' when s_uart_state = IDLE else '0'; + + tx_o <= s_data(0); + +end architecture rtl; diff --git a/uart_loop/sim/Makefile b/uart_loop/sim/Makefile new file mode 100644 index 0000000..b280847 --- /dev/null +++ b/uart_loop/sim/Makefile @@ -0,0 +1,30 @@ +DESIGN_NAME := uart_loop +LIB_SRC := ../../lib/rtl_components.vhd ../../lib/sim_components.vhd +RTL_SRC := ../../lib/user_components.vhd ../rtl/uart_tx.vhd ../rtl/uart_rx.vhd ../rtl/${DESIGN_NAME}.vhd +SIM_SRC := tb_${DESIGN_NAME}.vhd +SIM_FLAGS := --std=08 -fpsl --workdir=work + +.PHONY: all compile sim clean + +all: sim +compile: tb_${DESIGN_NAME} + +tb_${DESIGN_NAME}: ${LIB_SRC} ${RTL_SRC} ${SIM_SRC} + mkdir -p work + @echo "Analyze gatemate library ..." + ghdl -a ${SIM_FLAGS} --work=gatemate ${LIB_SRC} + @echo "Analyze testbench & design ..." + ghdl -a ${SIM_FLAGS} -Pwork ${RTL_SRC} ${SIM_SRC} + @echo "Elaborate testbench & design ..." + ghdl -e ${SIM_FLAGS} -Pwork $@ + +sim: tb_${DESIGN_NAME} + @echo "Run testbench ..." + ghdl -r ${SIM_FLAGS} -Pwork tb_${DESIGN_NAME} --assert-level=error + +work: + mkdir $@ + +clean: + @echo "Cleaning simulation files ..." + rm -rf tb_${DESIGN_NAME} *.o work/ diff --git a/uart_loop/sim/tb_uart_loop.vhd b/uart_loop/sim/tb_uart_loop.vhd new file mode 100644 index 0000000..b8b2544 --- /dev/null +++ b/uart_loop/sim/tb_uart_loop.vhd @@ -0,0 +1,96 @@ +library ieee ; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +use std.env.all; + + +entity tb_uart_loop is +end entity tb_uart_loop; + + +architecture sim of tb_uart_loop is + + signal s_clk : std_logic := '1'; + signal s_rst_n : std_logic := '0'; + + signal s_uart_rx : std_logic := '1'; + signal s_uart_tx : std_logic; + + constant c_baudrate : natural := 9600; + constant c_period_ns : time := 1000000000 / c_baudrate * ns; + + procedure uart_send ( data : in std_logic_vector(7 downto 0); + signal tx : out std_logic) is + begin + report "UART send: 0x" & to_hstring(data); + tx <= '0'; + wait for c_period_ns; + for i in 0 to 7 loop + tx <= data(i); + wait for c_period_ns; + end loop; + tx <= '1'; + wait for c_period_ns; + end procedure; + + procedure uart_recv ( data : out std_logic_vector(7 downto 0); + signal rx : in std_logic) is + begin + wait until not rx; + wait for c_period_ns; -- Skip start bit + wait for c_period_ns/2; + for i in 0 to 7 loop + data(i) := rx; + wait for c_period_ns; + end loop; + report "UART recv: 0x" & to_hstring(data); + end procedure; + +begin + + dut : entity work.uart_loop + port map ( + clk_i => s_clk, + rst_n_i => s_rst_n, + uart_rx_i => s_uart_rx, + uart_tx_o => s_uart_tx + ); + + s_rst_n <= '1' after 120 ns; + s_clk <= not s_clk after 50 ns; + + SendP : process is + variable v_data : std_logic_vector(7 downto 0); + begin + wait until s_rst_n; + wait until rising_edge(s_clk); + wait for 200 us; + -- First read all registers + for i in 0 to 255 loop + v_data := std_logic_vector(to_unsigned(i, 8)); + uart_send(v_data, s_uart_rx); + end loop; + wait; + end process; + + ReceiveP : process is + variable v_exp : std_logic_vector(7 downto 0); + variable v_data : std_logic_vector(7 downto 0); + begin + wait until s_rst_n; + wait until rising_edge(s_clk); + -- First read all registers + for i in 0 to 255 loop + v_exp := std_logic_vector(to_unsigned(i, 8)); + uart_recv(v_data, s_uart_tx); + assert v_data = v_exp + report "UART receive error, got 0x" & to_hstring(v_data) & ", expected 0x" & to_hstring(v_exp) + severity failure; + end loop; + wait for 200 us; + report "Simulation finished :-)"; + stop(0); + end process; + +end architecture; diff --git a/uart_loop/syn/Makefile b/uart_loop/syn/Makefile new file mode 100644 index 0000000..6bfb630 --- /dev/null +++ b/uart_loop/syn/Makefile @@ -0,0 +1,51 @@ +DESIGN_NAME := uart_loop +WORK_FILES := ../../lib/user_components.vhd ../rtl/uart_tx.vhd ../rtl/uart_rx.vhd ../rtl/${DESIGN_NAME}.vhd +GM_FILES := ../../lib/rtl_components.vhd +GHDL_FLAGS := --std=08 --workdir=build -Pbuild +YOSYSPIPE := -nomx8 -retime -nobram +# ATTENTION: -luttree option seems to mis-synthesize the design, broken with synth_gatemate? +PNRFLAGS := -om 2 +PNRTOOL := $(shell which p_r) + +.PHONY: all syn imp prog syn_sim imp_sim + +all: imp +syn: ${DESIGN_NAME}.v +imp: ${DESIGN_NAME}.bit + +build/work-obj08.cf: ${WORK_FILES} build/gatemate-obj08.cf + ghdl -a ${GHDL_FLAGS} --work=work ${WORK_FILES} + +build/gatemate-obj08.cf: ${GM_FILES} + mkdir -p build + ghdl -a ${GHDL_FLAGS} --work=gatemate ${GM_FILES} + +# Synthesis target for implementation +${DESIGN_NAME}.v: build/work-obj08.cf + yosys -m ghdl -p 'ghdl ${GHDL_FLAGS} --warn-no-binding --no-formal ${DESIGN_NAME}; synth_gatemate -top $(DESIGN_NAME) ${YOSYSPIPE} -vlog $@' \ + 2>&1 | tee build/yosys-report.txt + +# Implementation target for FPGA +${DESIGN_NAME}.bit: ${DESIGN_NAME}.v ${DESIGN_NAME}.ccf + cd build && \ + ${PNRTOOL} -i ../${DESIGN_NAME}.v -o $@ --ccf ../${DESIGN_NAME}.ccf $(PNRFLAGS) \ + 2>&1 | tee p_r-report.txt && \ + mv ${DESIGN_NAME}*.bit ../$@ + +# Post-synthesis simulation target +syn_sim: ${DESIGN_NAME}.v + iverilog -g2012 -o tb_${DESIGN_NAME}_syn.vvp ${DESIGN_NAME}.v tb_${DESIGN_NAME}.v /usr/local/share/yosys/gatemate/cells_sim.v + vvp -N tb_${DESIGN_NAME}_syn.vvp -fst + +# Post-implementation simulation target +imp_sim: ${DESIGN_NAME}.bit + iverilog -g2012 -o tb_${DESIGN_NAME}_imp.vvp build/${DESIGN_NAME}_00.v tb_${DESIGN_NAME}.v /opt/cc-toolchain-linux/bin/p_r/cpelib.v + vvp -N tb_${DESIGN_NAME}_imp.vvp -fst + +# FPGA FW load per JTAG +prog: ${DESIGN_NAME}.bit + openFPGALoader -b gatemate_evb_jtag $< + +clean : + echo "# Cleaning files" + rm -rf build ${DESIGN_NAME}.v ${DESIGN_NAME}_sim.v ${DESIGN_NAME}.vhd ${DESIGN_NAME}.bit *.vvp *.fst diff --git a/uart_loop/syn/tb_uart_loop.v b/uart_loop/syn/tb_uart_loop.v new file mode 100644 index 0000000..1016252 --- /dev/null +++ b/uart_loop/syn/tb_uart_loop.v @@ -0,0 +1,125 @@ +`timescale 1 ns/10 ps // time-unit = 1 ns, precision = 10 ps + +// simplified CC_PLL model +module CC_PLL #( + parameter REF_CLK = "", // e.g. "10.0" + parameter OUT_CLK = "", // e.g. "50.0" + parameter PERF_MD = "", // LOWPOWER, ECONOMY, SPEED + parameter LOW_JITTER = 1, + parameter CI_FILTER_CONST = 2, + parameter CP_FILTER_CONST = 4 +)( + input CLK_REF, CLK_FEEDBACK, USR_CLK_REF, + input USR_LOCKED_STDY_RST, USR_SET_SEL, + output USR_PLL_LOCKED_STDY, USR_PLL_LOCKED, + output CLK270, CLK180, CLK90, CLK0, CLK_REF_OUT +); + + reg r_pll_clk; + reg r_user_pll_locked; + + // OUT_FREQ = 1 MHz + integer clk_half_period = 500; + + initial begin + r_pll_clk = 1'b0; + r_user_pll_locked = 1'b1; + end + + always #clk_half_period r_pll_clk = ~r_pll_clk; + + assign CLK0 = r_pll_clk; + assign USR_PLL_LOCKED = r_user_pll_locked; + +endmodule + + +// simplified CC_CFG_END model +module CC_CFG_END ( + output CFG_END +); + + assign CFG_END = 1'b1; + +endmodule + +module tb_uart_loop; + + // DUT in/out + reg clk = 1'b0; + reg rst_n = 1'b1; + reg uart_rx; + wire uart_tx; + + // Testbench variables + reg [7:0] tx_data = 8'h0; + reg [7:0] rx_data = 8'h0; + + // Testbench 1/2 clock period + localparam clk_half_period = 50; + + // UART period calculation (9600 baud) + localparam uart_bit_period = 1000000000 / 9600; + localparam uart_bit_half_period = uart_bit_period/2; + + uart_loop UUT (.clk_i(clk), .rst_n_i(rst_n), .uart_rx_i(uart_rx), .uart_tx_o(uart_tx)); + + // set dumpfile + initial begin + $dumpfile ("tb_uart_loop.fst"); + $dumpvars (0, tb_uart_loop); + end + + // Setup simulation + initial begin + uart_rx = 1'b1; + #1 rst_n = 1'b0; + #120 rst_n = 1'b1; + end + + // Generate 10 mhz clock + always #clk_half_period clk = !clk; + + // Stimuli generator + initial + forever @(posedge rst_n) begin + uart_rx = 1'b1; + #uart_bit_period; + for (integer tx = 0; tx < 32; tx = tx + 1) begin + tx_data = tx; + $display ("UART send: 0x%h", tx_data); + uart_rx = 1'b0; + #uart_bit_period; + for (integer i = 0; i < 7; i = i + 1) begin + uart_rx = tx_data[i]; + #uart_bit_period; + end + uart_rx = 1'b1; + #uart_bit_period; + #uart_bit_period + #uart_bit_period; + end + end + + // Checker + initial begin + @(posedge rst_n) + for (reg [7:0] rx = 0; rx < 32; rx = rx + 1) begin + @(negedge uart_tx) + #uart_bit_period; + #uart_bit_half_period; + for (integer i = 0; i < 7; i = i + 1) begin + rx_data[i] = uart_tx; + #uart_bit_period; + end + assert (rx_data == rx) + $display("UART recv: 0x%h", rx_data); + else + $warning("UART receive error, got 0x%h, expected 0x%h", rx_data, rx); + end + $display ("UART tests finished"); + $finish; + end + + +endmodule diff --git a/uart_loop/syn/uart_loop.ccf b/uart_loop/syn/uart_loop.ccf new file mode 100644 index 0000000..5a1e39b --- /dev/null +++ b/uart_loop/syn/uart_loop.ccf @@ -0,0 +1,16 @@ +# Configuration for the Gatemate eval board + +Pin_in "clk_i" Loc = "IO_SB_A8" | SCHMITT_TRIGGER=true; +Pin_in "rst_n_i" Loc = "IO_EB_B0"; # SW3 + +Pin_in "uart_rx_i" Loc = "IO_NB_A1"; # PMODA IO3 +Pin_out "uart_tx_o" Loc = "IO_NB_A0"; # PMODA IO1 + +Pin_out "debug_o[0]" Loc = "IO_NB_A4"; +Pin_out "debug_o[1]" Loc = "IO_NB_A5"; +Pin_out "debug_o[2]" Loc = "IO_NB_A6"; +Pin_out "debug_o[3]" Loc = "IO_NB_A7"; +Pin_out "debug_o[4]" Loc = "IO_NB_B4"; +Pin_out "debug_o[5]" Loc = "IO_NB_B5"; +Pin_out "debug_o[6]" Loc = "IO_NB_B6"; +Pin_out "debug_o[7]" Loc = "IO_NB_B7"; diff --git a/uart_reg/syn/uart_reg.ccf b/uart_reg/syn/uart_reg.ccf index 05bd5a5..5a1e39b 100644 --- a/uart_reg/syn/uart_reg.ccf +++ b/uart_reg/syn/uart_reg.ccf @@ -4,7 +4,7 @@ Pin_in "clk_i" Loc = "IO_SB_A8" | SCHMITT_TRIGGER=true; Pin_in "rst_n_i" Loc = "IO_EB_B0"; # SW3 Pin_in "uart_rx_i" Loc = "IO_NB_A1"; # PMODA IO3 -Pin_out "uart_tx_o" Loc = "IO_NB_A2"; # PMODA IO5 +Pin_out "uart_tx_o" Loc = "IO_NB_A0"; # PMODA IO1 Pin_out "debug_o[0]" Loc = "IO_NB_A4"; Pin_out "debug_o[1]" Loc = "IO_NB_A5";