Browse Source

Update uart_reg to full reg file implementation

* uart_reg design implements a register file now which can be
  accessed by an UART with 9600 baud
* It has 8 registers storing values of one byte each.
* The first received byte on the axis in port contains command &
  address. In case of a write command, the payload has to follow
  with the next byte. In case of a read command, the value of the addressed
  register is returned on the axis out port.
* Register at address 0 is special. It contains the version
  and is read-only. Writes to that register are ignored.
main
T. Meissner 1 year ago
parent
commit
d63dfe6b4a
5 changed files with 222 additions and 29 deletions
  1. +135
    -0
      uart_reg/rtl/uart_ctrl.vhd
  2. +27
    -4
      uart_reg/rtl/uart_reg.vhd
  3. +1
    -1
      uart_reg/sim/Makefile
  4. +58
    -22
      uart_reg/sim/tb_uart_reg.vhd
  5. +1
    -2
      uart_reg/syn/Makefile

+ 135
- 0
uart_reg/rtl/uart_ctrl.vhd View File

@ -0,0 +1,135 @@
-- UART register
-- Register file with 8 registers storing values of one byte each.
--
-- The first received byte on the axis in port contains command & address:
--
-- 7 reserved
-- 6:4 register address
-- 3:0 command
-- 0x0 read
-- 0x1 write
--
-- In case of a write command, the payload has to follow
-- with the next byte.
--
-- In case of a read command, the value of the addressed
-- register is returned on the axis out port.
--
-- Register at address 0 is special. It contains the version
-- and is read-only. Writes to that register are ignored.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity uart_ctrl is
port (
-- globals
rst_n_i : in std_logic;
clk_i : in std_logic;
-- axis in
tdata_i : in std_logic_vector(7 downto 0);
tvalid_i : in std_logic;
tready_o : out std_logic;
-- axis out
tdata_o : out std_logic_vector(7 downto 0);
tvalid_o : out std_logic;
tready_i : in std_logic
);
end entity uart_ctrl;
architecture rtl of uart_ctrl is
type t_state is (IDLE, GET_CMD, RECV_DATA, SEND_DATA);
signal s_state : t_state;
subtype t_reg is std_logic_vector(7 downto 0);
type t_reg_file is array (1 to 7) of t_reg;
signal s_reg_file : t_reg_file;
constant c_version : t_reg := x"01";
signal s_reg_addr : natural range 0 to 7;
signal s_reg_data : t_reg;
subtype t_cmd is std_ulogic_vector(3 downto 0);
constant c_read : t_cmd := x"0";
constant c_write : t_cmd := x"1";
alias a_tdata_cmd is tdata_i(3 downto 0);
alias a_tdata_addr is tdata_i(6 downto 4);
begin
-- Register memory, omitted reset of memory during synthesis
-- for better RAM detection
process (clk_i, rst_n_i) is
begin
if (not rst_n_i) then
-- synthesis translate_off
s_reg_file <= (others => (others => '0'));
-- synthesis translate_on
s_reg_data <= (others => '0');
elsif (rising_edge(clk_i)) then
-- Write
if (s_state = RECV_DATA and tvalid_i = '1') then
-- Ignore writes to version register
if (s_reg_addr /= 0) then
s_reg_file(s_reg_addr) <= tdata_i;
end if;
end if;
-- Always read, regardless of write or read command
if (s_state = GET_CMD) then
if (s_reg_addr /= 0) then
s_reg_data <= s_reg_file(s_reg_addr);
end if;
end if;
end if;
end process;
-- Control state machine
process (clk_i, rst_n_i) is
begin
if (not rst_n_i) then
s_state <= IDLE;
s_reg_addr <= 0;
elsif (rising_edge(clk_i)) then
case s_state is
when IDLE =>
if (tvalid_i) then
s_state <= GET_CMD;
s_reg_addr <= to_integer(unsigned(a_tdata_addr));
end if;
when GET_CMD =>
if (a_tdata_cmd = c_read) then
s_state <= SEND_DATA;
elsif (a_tdata_cmd = c_write) then
s_state <= RECV_DATA;
else
s_state <= IDLE;
end if;
when RECV_DATA =>
if (tvalid_i) then
s_state <= IDLE;
end if;
when SEND_DATA =>
if (tready_i) then
s_state <= IDLE;
end if;
when others =>
null;
end case;
end if;
end process;
tready_o <= '1' when s_state = GET_CMD or s_state = RECV_DATA else '0';
tdata_o <= c_version when s_reg_addr = 0 else s_reg_data;
tvalid_o <= '1' when s_state = SEND_DATA else '0';
end architecture;

+ 27
- 4
uart_reg/rtl/uart_reg.vhd View File

@ -1,4 +1,8 @@
-- This design implements a simple UART loop with 9600 baud
-- 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 ;
@ -31,6 +35,10 @@ architecture rtl of uart_reg is
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
@ -74,6 +82,21 @@ begin
rx_i => uart_rx_i
);
uart_ctrl : entity work.uart_ctrl
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
);
uart_tx : entity work.uart_tx
generic map (
CLK_DIV => 104
@ -83,9 +106,9 @@ begin
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,
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
);


+ 1
- 1
uart_reg/sim/Makefile View File

@ -1,6 +1,6 @@
DESIGN_NAME := uart_reg
LIB_SRC := ../../lib/rtl_components.vhd ../../lib/sim_components.vhd
RTL_SRC := ../rtl/uart_tx.vhd ../rtl/uart_rx.vhd ../rtl/${DESIGN_NAME}.vhd
RTL_SRC := ../rtl/uart_tx.vhd ../rtl/uart_rx.vhd ../rtl/uart_ctrl.vhd ../rtl/${DESIGN_NAME}.vhd
SIM_SRC := tb_${DESIGN_NAME}.vhd
SIM_FLAGS := --std=08 -fpsl --workdir=work


+ 58
- 22
uart_reg/sim/tb_uart_reg.vhd View File

@ -20,6 +20,33 @@ architecture sim of tb_uart_reg is
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_reg
@ -39,37 +66,46 @@ 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;
-- First read all registers
for i in 0 to 7 loop
v_data := std_logic_vector(to_unsigned(i, 4)) & x"0";
uart_send(v_data, s_uart_rx);
end loop;
-- Then write all registers
for i in 0 to 7 loop
v_data := std_logic_vector(to_unsigned(i, 4)) & x"1";
uart_send(v_data, s_uart_rx);
uart_send(x"FF", s_uart_rx);
end loop;
-- Finally read all registers again after write
for i in 0 to 7 loop
v_data := std_logic_vector(to_unsigned(i, 4)) & x"0";
uart_send(v_data, s_uart_rx);
end loop;
wait;
end process;
ReceiveP : process is
type t_exp is array (0 to 7) of std_logic_vector(7 downto 0);
variable v_exp : t_exp;
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)
-- First read all registers
v_exp := (0 => x"01", others => x"00");
for i in 0 to 7 loop
uart_recv(v_data, s_uart_tx);
assert v_data = v_exp(i)
report "UART receive error, got 0x" & to_hstring(v_data) & ", expected 0x" & to_hstring(v_exp(i))
severity failure;
end loop;
-- Finally read all registers again after write
v_exp := (0 => x"01", others => x"FF");
for i in 0 to 7 loop
uart_recv(v_data, s_uart_tx);
assert v_data = v_exp(i)
report "UART receive error, got 0x" & to_hstring(v_data) & ", expected 0x" & to_hstring(v_exp(i))
severity failure;
end loop;
wait for 200 us;


+ 1
- 2
uart_reg/syn/Makefile View File

@ -1,5 +1,5 @@
DESIGN_NAME := uart_reg
WORK_FILES := ../rtl/uart_tx.vhd ../rtl/uart_rx.vhd ../rtl/uart_reg.vhd
WORK_FILES := ../rtl/uart_ctrl.vhd ../rtl/uart_tx.vhd ../rtl/uart_rx.vhd ../rtl/uart_reg.vhd
GM_FILES := ../../lib/rtl_components.vhd
GHDL_FLAGS := --std=08 --workdir=build -Pbuild
YOSYSPIPE := -nomx8 -retime
@ -22,7 +22,6 @@ build/gatemate-obj08.cf: ${GM_FILES}
# Synthesis target for implementation
${DESIGN_NAME}.v: build/work-obj08.cf
ghdl --synth ${GHDL_FLAGS} ${DESIGN_NAME} > ${DESIGN_NAME}.vhd
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


Loading…
Cancel
Save