Browse Source

Add uart tx/rx modules, add make targets and testbenches for rtl, post-syn & post-imp simulations

main
T. Meissner 1 year ago
parent
commit
61affc8b49
8 changed files with 460 additions and 9 deletions
  1. +41
    -3
      uart_reg/rtl/uart_reg.vhd
  2. +105
    -0
      uart_reg/rtl/uart_rx.vhd
  3. +102
    -0
      uart_reg/rtl/uart_tx.vhd
  4. +30
    -0
      uart_reg/sim/Makefile
  5. +83
    -0
      uart_reg/sim/tb_uart_reg.vhd
  6. +13
    -5
      uart_reg/syn/Makefile
  7. +80
    -0
      uart_reg/syn/tb_uart_reg.v
  8. +6
    -1
      uart_reg/syn/uart_reg.ccf

+ 41
- 3
uart_reg/rtl/uart_reg.vhd View File

@ -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;

+ 105
- 0
uart_reg/rtl/uart_rx.vhd View File

@ -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;

+ 102
- 0
uart_reg/rtl/uart_tx.vhd View File

@ -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;

+ 30
- 0
uart_reg/sim/Makefile View File

@ -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/

+ 83
- 0
uart_reg/sim/tb_uart_reg.vhd View File

@ -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;

+ 13
- 5
uart_reg/syn/Makefile View File

@ -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

+ 80
- 0
uart_reg/syn/tb_uart_reg.v View File

@ -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

+ 6
- 1
uart_reg/syn/uart_reg.ccf View File

@ -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";

Loading…
Cancel
Save