| @ -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; | |||
| @ -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; | |||
| @ -0,0 +1,30 @@ | |||
| DESIGN_NAME := uart_reg | |||
| LIB_SRC := ../../lib/components.vhd ../../lib/sim_components.vhd | |||
| RTL_SRC := ../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/ | |||
| @ -0,0 +1,83 @@ | |||
| library ieee ; | |||
| use ieee.std_logic_1164.all; | |||
| use ieee.numeric_std.all; | |||
| use std.env.all; | |||
| entity tb_uart_reg is | |||
| end entity tb_uart_reg; | |||
| architecture sim of tb_uart_reg is | |||
| signal s_clk : std_logic := '1'; | |||
| signal s_rst_n : std_logic := '0'; | |||
| signal s_led_n : std_logic_vector(3 downto 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; | |||
| begin | |||
| dut : entity work.uart_reg | |||
| port map ( | |||
| clk_i => s_clk, | |||
| rst_n_i => s_rst_n, | |||
| uart_rx_i => s_uart_rx, | |||
| uart_tx_o => s_uart_tx, | |||
| led_n_o => s_led_n | |||
| ); | |||
| 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; | |||
| for tx in 0 to 255 loop | |||
| v_data := std_logic_vector(to_unsigned(tx, 8)); | |||
| report "UART send: 0x" & to_hstring(v_data); | |||
| s_uart_rx <= '0'; | |||
| wait for c_period_ns; | |||
| for i in 0 to 7 loop | |||
| s_uart_rx <= v_data(i); | |||
| wait for c_period_ns; | |||
| end loop; | |||
| s_uart_rx <= '1'; | |||
| wait for c_period_ns; | |||
| end loop; | |||
| wait; | |||
| end process; | |||
| ReceiveP : process is | |||
| variable v_data : std_logic_vector(7 downto 0); | |||
| begin | |||
| wait until s_rst_n; | |||
| wait until rising_edge(s_clk); | |||
| for rx in 0 to 255 loop | |||
| wait until not s_uart_tx; | |||
| wait for c_period_ns; -- Skip start bit | |||
| wait for c_period_ns/2; | |||
| for i in 0 to 7 loop | |||
| v_data(i) := s_uart_tx; | |||
| wait for c_period_ns; | |||
| end loop; | |||
| report "UART recv: 0x" & to_hstring(v_data); | |||
| assert v_data = std_logic_vector(to_unsigned(rx, 8)) | |||
| report "UART receive error, got 0x" & to_hstring(v_data) & ", expected 0x" & to_hstring(v_data) | |||
| severity failure; | |||
| end loop; | |||
| wait for 200 us; | |||
| report "Simulation finished :-)"; | |||
| stop(0); | |||
| end process; | |||
| end architecture; | |||
| @ -0,0 +1,80 @@ | |||
| `timescale 1 ns/10 ps // time-unit = 1 ns, precision = 10 ps | |||
| module tb_uart_reg; | |||
| reg clk = 0; | |||
| reg rst_n; | |||
| reg uart_rx; | |||
| wire uart_tx; | |||
| reg [7:0] tx_data = 0; | |||
| reg [7:0] rx_data = 0; | |||
| wire [3:0] led_n; | |||
| localparam clk_half_period = 50; | |||
| localparam uart_bit_period = 1000000000 / 9600; | |||
| localparam uart_bit_half_period = uart_bit_period/2; | |||
| uart_reg UUT (.clk_i(clk), .rst_n_i(rst_n), .uart_rx_i(uart_rx), .uart_tx_o(uart_tx), .led_n_o(led_n)); | |||
| // set dumpfile | |||
| initial begin | |||
| $dumpfile ("tb_uart_reg.fst"); | |||
| $dumpvars (0, tb_uart_reg); | |||
| end | |||
| // setup simulation | |||
| initial begin | |||
| rst_n = 1; | |||
| #1 rst_n = 0; | |||
| #20 rst_n = 1; | |||
| end | |||
| // generate clock with 100 mhz | |||
| always #clk_half_period clk = !clk; | |||
| initial begin | |||
| uart_rx = 1'b1; | |||
| end | |||
| initial | |||
| forever @(posedge rst_n) begin | |||
| uart_rx = 1'b1; | |||
| #uart_bit_period; | |||
| for (integer tx = 0; tx < 16; 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 | |||
| always begin | |||
| wait (rst_n) | |||
| for (reg [7:0] rx = 0; rx < 16; 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 | |||