From 61affc8b49c04de7ffcead9e0ba6d7134008f66e Mon Sep 17 00:00:00 2001 From: tmeissner Date: Wed, 28 Dec 2022 00:16:38 +0100 Subject: [PATCH] Add uart tx/rx modules, add make targets and testbenches for rtl, post-syn & post-imp simulations --- uart_reg/rtl/uart_reg.vhd | 44 ++++++++++++++- uart_reg/rtl/uart_rx.vhd | 105 +++++++++++++++++++++++++++++++++++ uart_reg/rtl/uart_tx.vhd | 102 ++++++++++++++++++++++++++++++++++ uart_reg/sim/Makefile | 30 ++++++++++ uart_reg/sim/tb_uart_reg.vhd | 83 +++++++++++++++++++++++++++ uart_reg/syn/Makefile | 18 ++++-- uart_reg/syn/tb_uart_reg.v | 80 ++++++++++++++++++++++++++ uart_reg/syn/uart_reg.ccf | 7 ++- 8 files changed, 460 insertions(+), 9 deletions(-) create mode 100644 uart_reg/rtl/uart_rx.vhd create mode 100644 uart_reg/rtl/uart_tx.vhd create mode 100644 uart_reg/sim/Makefile create mode 100644 uart_reg/sim/tb_uart_reg.vhd create mode 100644 uart_reg/syn/tb_uart_reg.v diff --git a/uart_reg/rtl/uart_reg.vhd b/uart_reg/rtl/uart_reg.vhd index 5edf26c..6b9ee03 100644 --- a/uart_reg/rtl/uart_reg.vhd +++ b/uart_reg/rtl/uart_reg.vhd @@ -16,7 +16,8 @@ port ( rst_n_i : in std_logic; -- SW3 button uart_rx_i : in std_logic; uart_tx_o : out std_logic; - led_n_o : out std_logic_vector(2 downto 0) -- LED1..LED2 + led_n_o : out std_logic_vector(3 downto 0); -- LED1..LED2 + debug_o : out std_logic_vector(3 downto 0) ); end entity uart_reg; @@ -30,6 +31,11 @@ architecture rtl of uart_reg is 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 : std_logic; + begin pll : CC_PLL @@ -57,12 +63,44 @@ begin 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 + ); + + 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_rx_tdata, + tvalid_i => s_uart_rx_tvalid, + tready_o => s_uart_rx_tready, + -- uart interface + tx_o => uart_tx_o + ); + s_rst_n <= rst_n_i and s_pll_lock and s_cfg_end; -- Start with simple loop - uart_tx_o <= uart_rx_i; +-- uart_tx_o <= uart_rx_i; -- Debug output - led_n_o <= s_rst_n & not (s_pll_lock, s_cfg_end); + led_n_o <= uart_rx_i & s_rst_n & not (s_pll_lock, s_cfg_end); end architecture; diff --git a/uart_reg/rtl/uart_rx.vhd b/uart_reg/rtl/uart_rx.vhd new file mode 100644 index 0000000..680e747 --- /dev/null +++ b/uart_reg/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_reg/rtl/uart_tx.vhd b/uart_reg/rtl/uart_tx.vhd new file mode 100644 index 0000000..5ce450c --- /dev/null +++ b/uart_reg/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_reg/sim/Makefile b/uart_reg/sim/Makefile new file mode 100644 index 0000000..664b36d --- /dev/null +++ b/uart_reg/sim/Makefile @@ -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/ diff --git a/uart_reg/sim/tb_uart_reg.vhd b/uart_reg/sim/tb_uart_reg.vhd new file mode 100644 index 0000000..d19c85d --- /dev/null +++ b/uart_reg/sim/tb_uart_reg.vhd @@ -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; diff --git a/uart_reg/syn/Makefile b/uart_reg/syn/Makefile index 06d8c34..f62289c 100644 --- a/uart_reg/syn/Makefile +++ b/uart_reg/syn/Makefile @@ -1,5 +1,5 @@ DESIGN_NAME := uart_reg -WORK_FILES := ../rtl/uart_reg.vhd +WORK_FILES := ../rtl/uart_tx.vhd ../rtl/uart_rx.vhd ../rtl/uart_reg.vhd GM_FILES := ../../lib/components.vhd GHDL_FLAGS := --std=08 --workdir=build -Pbuild YOSYSPIPE := -nomx8 -luttree -retime @@ -23,15 +23,23 @@ ${DESIGN_NAME}.v: build/work-obj08.cf yosys -m ghdl -p 'ghdl ${GHDL_FLAGS} --no-formal ${DESIGN_NAME}; synth_gatemate -top $(DESIGN_NAME) ${YOSYSPIPE} -vlog $@' \ 2>&1 | tee build/yosys-report.txt -${DESIGN_NAME}.bit: ${DESIGN_NAME}.v ${DESIGN_NAME}.ccf +build/${DESIGN_NAME}_00.v ${DESIGN_NAME}.bit: ${DESIGN_NAME}.v ${DESIGN_NAME}.ccf cd build && \ - ${PNRTOOL} -i ../${DESIGN_NAME}.v -o $@ --ccf ../${DESIGN_NAME}.ccf $(PNRFLAGS) \ + ${PNRTOOL} -i ../${DESIGN_NAME}.v -o ${DESIGN_NAME}.bit --ccf ../${DESIGN_NAME}.ccf $(PNRFLAGS) \ 2>&1 | tee p_r-report.txt && \ - mv ${DESIGN_NAME}*.bit ../$@ + mv ${DESIGN_NAME}*.bit ../${DESIGN_NAME}.bit + +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 + +imp_sim: build/${DESIGN_NAME}_00.v + 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 prog: ${DESIGN_NAME}.bit openFPGALoader -b gatemate_evb_jtag $< clean : echo "# Cleaning files" - rm -rf build ${DESIGN_NAME}.v ${DESIGN_NAME}.bit + rm -rf build ${DESIGN_NAME}.v ${DESIGN_NAME}.bit *.vvp *.fst diff --git a/uart_reg/syn/tb_uart_reg.v b/uart_reg/syn/tb_uart_reg.v new file mode 100644 index 0000000..4057240 --- /dev/null +++ b/uart_reg/syn/tb_uart_reg.v @@ -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 diff --git a/uart_reg/syn/uart_reg.ccf b/uart_reg/syn/uart_reg.ccf index 1d48135..0209c49 100644 --- a/uart_reg/syn/uart_reg.ccf +++ b/uart_reg/syn/uart_reg.ccf @@ -6,7 +6,7 @@ Pin_in "rst_n_i" Loc = "IO_EB_B0"; # SW3 Pin_out "led_n_o[0]" Loc = "IO_EB_B1"; # LED D1 Pin_out "led_n_o[1]" Loc = "IO_EB_B2"; # LED D2 Pin_out "led_n_o[2]" Loc = "IO_EB_B3"; # LED D3 -#Pin_out "led_n_o[3]" Loc = "IO_EB_B4"; # LED D4 +Pin_out "led_n_o[3]" Loc = "IO_EB_B4"; # LED D4 #Pin_out "led_n_o[4]" Loc = "IO_EB_B5"; # LED D5 #Pin_out "led_n_o[5]" Loc = "IO_EB_B6"; # LED D6 #Pin_out "led_n_o[6]" Loc = "IO_EB_B7"; # LED D7 @@ -14,3 +14,8 @@ Pin_out "led_n_o[2]" Loc = "IO_EB_B3"; # LED D3 Pin_in "uart_rx_i" Loc = "IO_NB_A1"; # PMODA IO3 Pin_out "uart_tx_o" Loc = "IO_NB_A2"; # PMODA IO5 + +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";