Commit b34b5008 authored by Tomasz Wlostowski's avatar Tomasz Wlostowski Committed by Tristan Gingold

wishbone: UART now supports configurable FIFOs

parent 5f92798d
#!/bin/bash
mkdir -p doc
wbgen2 -D ./doc/wb_simple_uart.html -V simple_uart_wb.vhd -p simple_uart_pkg.vhd --cstyle struct -C wb_uart.h --hstyle record --lang vhdl simple_uart_wb.wb
wbgen2 -D ./doc/wb_simple_uart.html -V simple_uart_wb.vhd -p simple_uart_pkg.vhd -K ../../../testbench/wishbone/include/wb_uart_regs.vh --cstyle defines -C wb_uart.h --hstyle record --lang vhdl simple_uart_wb.wb
......@@ -3,7 +3,7 @@
---------------------------------------------------------------------------------------
-- File : simple_uart_pkg.vhd
-- Author : auto-generated by wbgen2 from simple_uart_wb.wb
-- Created : Tue Aug 15 10:16:30 2017
-- Created : Tue Aug 25 17:17:50 2020
-- Standard : VHDL'87
---------------------------------------------------------------------------------------
-- THIS FILE WAS GENERATED BY wbgen2 FROM SOURCE FILE simple_uart_wb.wb
......@@ -22,78 +22,134 @@ package uart_wbgen2_pkg is
type t_uart_in_registers is record
sr_tx_busy_i : std_logic;
sr_rx_rdy_i : std_logic;
sr_rx_fifo_supported_i : std_logic;
sr_tx_fifo_supported_i : std_logic;
sr_rx_fifo_valid_i : std_logic;
sr_tx_fifo_empty_i : std_logic;
sr_tx_fifo_full_i : std_logic;
sr_rx_fifo_overflow_i : std_logic;
sr_rx_fifo_bytes_i : std_logic_vector(7 downto 0);
rdr_rx_data_i : std_logic_vector(7 downto 0);
host_tdr_rdy_i : std_logic;
host_rdr_data_i : std_logic_vector(7 downto 0);
host_rdr_rdy_i : std_logic;
host_rdr_count_i : std_logic_vector(15 downto 0);
end record;
end record;
constant c_uart_in_registers_init_value: t_uart_in_registers := (
sr_tx_busy_i => '0',
sr_rx_rdy_i => '0',
sr_rx_fifo_supported_i => '0',
sr_tx_fifo_supported_i => '0',
sr_rx_fifo_valid_i => '0',
sr_tx_fifo_empty_i => '0',
sr_tx_fifo_full_i => '0',
sr_rx_fifo_overflow_i => '0',
sr_rx_fifo_bytes_i => (others => '0'),
rdr_rx_data_i => (others => '0'),
host_tdr_rdy_i => '0',
host_rdr_data_i => (others => '0'),
host_rdr_rdy_i => '0',
host_rdr_count_i => (others => '0')
);
-- Output registers (WB slave -> user design)
type t_uart_out_registers is record
bcr_o : std_logic_vector(31 downto 0);
bcr_wr_o : std_logic;
tdr_tx_data_o : std_logic_vector(7 downto 0);
tdr_tx_data_wr_o : std_logic;
host_tdr_data_o : std_logic_vector(7 downto 0);
host_tdr_data_wr_o : std_logic;
end record;
constant c_uart_out_registers_init_value: t_uart_out_registers := (
bcr_o => (others => '0'),
bcr_wr_o => '0',
tdr_tx_data_o => (others => '0'),
tdr_tx_data_wr_o => '0',
host_tdr_data_o => (others => '0'),
host_tdr_data_wr_o => '0'
);
function "or" (left, right: t_uart_in_registers) return t_uart_in_registers;
function f_x_to_zero (x:std_logic) return std_logic;
function f_x_to_zero (x:std_logic_vector) return std_logic_vector;
);
-- Output registers (WB slave -> user design)
type t_uart_out_registers is record
sr_rx_fifo_overflow_o : std_logic;
sr_rx_fifo_overflow_load_o : std_logic;
bcr_o : std_logic_vector(31 downto 0);
bcr_wr_o : std_logic;
tdr_tx_data_o : std_logic_vector(7 downto 0);
tdr_tx_data_wr_o : std_logic;
host_tdr_data_o : std_logic_vector(7 downto 0);
host_tdr_data_wr_o : std_logic;
cr_rx_fifo_purge_o : std_logic;
cr_tx_fifo_purge_o : std_logic;
end record;
constant c_uart_out_registers_init_value: t_uart_out_registers := (
sr_rx_fifo_overflow_o => '0',
sr_rx_fifo_overflow_load_o => '0',
bcr_o => (others => '0'),
bcr_wr_o => '0',
tdr_tx_data_o => (others => '0'),
tdr_tx_data_wr_o => '0',
host_tdr_data_o => (others => '0'),
host_tdr_data_wr_o => '0',
cr_rx_fifo_purge_o => '0',
cr_tx_fifo_purge_o => '0'
);
function "or" (left, right: t_uart_in_registers) return t_uart_in_registers;
function f_x_to_zero (x:std_logic) return std_logic;
function f_x_to_zero (x:std_logic_vector) return std_logic_vector;
component simple_uart_wb is
port (
rst_n_i : in std_logic;
clk_sys_i : in std_logic;
wb_adr_i : in std_logic_vector(2 downto 0);
wb_dat_i : in std_logic_vector(31 downto 0);
wb_dat_o : out std_logic_vector(31 downto 0);
wb_cyc_i : in std_logic;
wb_sel_i : in std_logic_vector(3 downto 0);
wb_stb_i : in std_logic;
wb_we_i : in std_logic;
wb_ack_o : out std_logic;
wb_err_o : out std_logic;
wb_rty_o : out std_logic;
wb_stall_o : out std_logic;
rdr_rack_o : out std_logic;
host_rack_o : out std_logic;
regs_i : in t_uart_in_registers;
regs_o : out t_uart_out_registers
);
end component;
end package;
package body uart_wbgen2_pkg is
function f_x_to_zero (x:std_logic) return std_logic is
begin
if x = '1' then
return '1';
else
return '0';
end if;
if x = '1' then
return '1';
else
return '0';
end if;
end function;
function f_x_to_zero (x:std_logic_vector) return std_logic_vector is
variable tmp: std_logic_vector(x'length-1 downto 0);
variable tmp: std_logic_vector(x'length-1 downto 0);
begin
for i in 0 to x'length-1 loop
if x(i) = '1' then
tmp(i):= '1';
else
tmp(i):= '0';
end if;
end loop;
return tmp;
for i in 0 to x'length-1 loop
if(x(i) = 'X' or x(i) = 'U') then
tmp(i):= '0';
else
tmp(i):=x(i);
end if;
end loop;
return tmp;
end function;
function "or" (left, right: t_uart_in_registers) return t_uart_in_registers is
variable tmp: t_uart_in_registers;
variable tmp: t_uart_in_registers;
begin
tmp.sr_tx_busy_i := f_x_to_zero(left.sr_tx_busy_i) or f_x_to_zero(right.sr_tx_busy_i);
tmp.sr_rx_rdy_i := f_x_to_zero(left.sr_rx_rdy_i) or f_x_to_zero(right.sr_rx_rdy_i);
tmp.rdr_rx_data_i := f_x_to_zero(left.rdr_rx_data_i) or f_x_to_zero(right.rdr_rx_data_i);
tmp.host_tdr_rdy_i := f_x_to_zero(left.host_tdr_rdy_i) or f_x_to_zero(right.host_tdr_rdy_i);
tmp.host_rdr_data_i := f_x_to_zero(left.host_rdr_data_i) or f_x_to_zero(right.host_rdr_data_i);
tmp.host_rdr_rdy_i := f_x_to_zero(left.host_rdr_rdy_i) or f_x_to_zero(right.host_rdr_rdy_i);
tmp.host_rdr_count_i := f_x_to_zero(left.host_rdr_count_i) or f_x_to_zero(right.host_rdr_count_i);
return tmp;
tmp.sr_tx_busy_i := f_x_to_zero(left.sr_tx_busy_i) or f_x_to_zero(right.sr_tx_busy_i);
tmp.sr_rx_rdy_i := f_x_to_zero(left.sr_rx_rdy_i) or f_x_to_zero(right.sr_rx_rdy_i);
tmp.sr_rx_fifo_supported_i := f_x_to_zero(left.sr_rx_fifo_supported_i) or f_x_to_zero(right.sr_rx_fifo_supported_i);
tmp.sr_tx_fifo_supported_i := f_x_to_zero(left.sr_tx_fifo_supported_i) or f_x_to_zero(right.sr_tx_fifo_supported_i);
tmp.sr_rx_fifo_valid_i := f_x_to_zero(left.sr_rx_fifo_valid_i) or f_x_to_zero(right.sr_rx_fifo_valid_i);
tmp.sr_tx_fifo_empty_i := f_x_to_zero(left.sr_tx_fifo_empty_i) or f_x_to_zero(right.sr_tx_fifo_empty_i);
tmp.sr_tx_fifo_full_i := f_x_to_zero(left.sr_tx_fifo_full_i) or f_x_to_zero(right.sr_tx_fifo_full_i);
tmp.sr_rx_fifo_overflow_i := f_x_to_zero(left.sr_rx_fifo_overflow_i) or f_x_to_zero(right.sr_rx_fifo_overflow_i);
tmp.sr_rx_fifo_bytes_i := f_x_to_zero(left.sr_rx_fifo_bytes_i) or f_x_to_zero(right.sr_rx_fifo_bytes_i);
tmp.rdr_rx_data_i := f_x_to_zero(left.rdr_rx_data_i) or f_x_to_zero(right.rdr_rx_data_i);
tmp.host_tdr_rdy_i := f_x_to_zero(left.host_tdr_rdy_i) or f_x_to_zero(right.host_tdr_rdy_i);
tmp.host_rdr_data_i := f_x_to_zero(left.host_rdr_data_i) or f_x_to_zero(right.host_rdr_data_i);
tmp.host_rdr_rdy_i := f_x_to_zero(left.host_rdr_rdy_i) or f_x_to_zero(right.host_rdr_rdy_i);
tmp.host_rdr_count_i := f_x_to_zero(left.host_rdr_count_i) or f_x_to_zero(right.host_rdr_count_i);
return tmp;
end function;
end package body;
This diff is collapsed.
......@@ -28,9 +28,84 @@ peripheral {
access_bus = READ_ONLY;
access_dev = WRITE_ONLY;
};
field {
name = "RX FIFO supported";
description = "1: UART supports RX FIFO";
prefix = "RX_FIFO_SUPPORTED";
type = BIT;
access_bus = READ_ONLY;
access_dev = WRITE_ONLY;
};
field {
name = "TX FIFO supported";
description = "1: UART supports TX FIFO";
prefix = "TX_FIFO_SUPPORTED";
type = BIT;
access_bus = READ_ONLY;
access_dev = WRITE_ONLY;
};
field {
name = "RX FIFO data valid";
description = "1: there's some data in the RX FIFO";
prefix = "RX_FIFO_VALID";
type = BIT;
access_bus = READ_ONLY;
access_dev = WRITE_ONLY;
};
field {
name = "TX FIFO empty";
description = "1: TX FIFO is empty";
prefix = "TX_FIFO_EMPTY";
type = BIT;
access_bus = READ_ONLY;
access_dev = WRITE_ONLY;
};
field {
name = "TX FIFO full";
description = "1: TX FIFO is full";
prefix = "TX_FIFO_FULL";
type = BIT;
access_bus = READ_ONLY;
access_dev = WRITE_ONLY;
};
field {
name = "RX FIFO overflow";
description = "1: RX FIFO overflow occured (latched bit, write 1 to clear)";
prefix = "RX_FIFO_OVERFLOW";
type = BIT;
access_bus = READ_WRITE;
access_dev = READ_WRITE;
load = LOAD_EXT;
};
field {
name = "RX FIFO data count";
description = "Number of bytes currently in the RX FIFO";
prefix = "RX_FIFO_BYTES";
type = SLV;
size = 8;
access_bus = READ_ONLY;
access_dev = WRITE_ONLY;
};
};
reg {
name = "Baudrate control register";
description = "Register controlling the UART baudrate";
......@@ -122,4 +197,28 @@ peripheral {
access_bus=READ_ONLY;
};
};
};
\ No newline at end of file
reg {
name = "UART General Control Register";
prefix = "CR";
field {
name = "RX FIFO purge";
description = "write 1: clears RX FIFO";
prefix = "RX_FIFO_PURGE";
type = MONOSTABLE;
};
field {
name = "TX FIFO purge";
description = "write 1: clears TX FIFO";
prefix = "TX_FIFO_PURGE";
type = MONOSTABLE;
};
};
};
This diff is collapsed.
......@@ -39,11 +39,14 @@ entity xwb_simple_uart is
generic (
g_WITH_VIRTUAL_UART : boolean := TRUE;
g_WITH_PHYSICAL_UART : boolean := TRUE;
g_WITH_PHYSICAL_UART_FIFO : boolean := false;
g_TX_FIFO_SIZE : integer := 0;
g_RX_FIFO_SIZE : integer := 0;
g_INTERFACE_MODE : t_wishbone_interface_mode := CLASSIC;
g_ADDRESS_GRANULARITY : t_wishbone_address_granularity := WORD;
g_VUART_FIFO_SIZE : integer := 1024;
g_PRESET_BCR : integer := 0
);
);
port (
clk_sys_i : in std_logic;
......@@ -71,6 +74,9 @@ begin -- arch
g_INTERFACE_MODE => g_INTERFACE_MODE,
g_ADDRESS_GRANULARITY => g_ADDRESS_GRANULARITY,
g_VUART_FIFO_SIZE => g_VUART_FIFO_SIZE,
g_WITH_PHYSICAL_UART_FIFO => g_WITH_PHYSICAL_UART_FIFO,
g_TX_FIFO_SIZE => g_TX_FIFO_SIZE,
g_RX_FIFO_SIZE => g_RX_FIFO_SIZE,
g_PRESET_BCR => g_PRESET_BCR)
port map (
clk_sys_i => clk_sys_i,
......
......@@ -944,13 +944,19 @@ package wishbone_pkg is
uart_txd_o : out std_logic);
end component;
component xwb_simple_uart
generic (
g_with_virtual_uart : boolean := false;
g_with_physical_uart : boolean := true;
g_interface_mode : t_wishbone_interface_mode := CLASSIC;
g_address_granularity : t_wishbone_address_granularity := WORD;
g_vuart_fifo_size : integer := 1024);
g_WITH_VIRTUAL_UART : boolean := TRUE;
g_WITH_PHYSICAL_UART : boolean := TRUE;
g_WITH_PHYSICAL_UART_FIFO : boolean := false;
g_TX_FIFO_SIZE : integer := 0;
g_RX_FIFO_SIZE : integer := 0;
g_INTERFACE_MODE : t_wishbone_interface_mode := CLASSIC;
g_ADDRESS_GRANULARITY : t_wishbone_address_granularity := WORD;
g_VUART_FIFO_SIZE : integer := 1024;
g_PRESET_BCR : integer := 0 );
port (
clk_sys_i : in std_logic;
rst_n_i : in std_logic;
......
sim_tool = "modelsim"
top_module="main"
action = "simulation"
target = "xilinx"
fetchto = "../../ip_cores"
vcom_opt="-mixedsvvh l -2008"
sim_top="main"
syn_device="xc7k70t"
include_dirs=["../../../sim", "../include" ]
files = [ "main.sv" ]
modules = { "local" : [ "../../../" ] }
//------------------------------------------------------------------------------
// Copyright CERN 2018
//------------------------------------------------------------------------------
// Copyright and related rights are licensed under the Solderpad Hardware
// 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
// http://solderpad.org/licenses/SHL-2.0.
// Unless required by applicable law or agreed to in writing, software,
// hardware and materials distributed under this 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.
//------------------------------------------------------------------------------
`timescale 1ps/1ps
`include "vhd_wishbone_master.svh"
`include "wb_uart_regs.vh"
import wishbone_pkg::*;
module dupa;
xwb_simple_uart dut();
endmodule // dupa
class IBusDevice;
CBusAccessor m_acc;
uint64_t m_base;
function new ( CBusAccessor acc, uint64_t base );
m_acc =acc;
m_base = base;
endfunction // new
virtual task write32( uint32_t addr, uint32_t val );
m_acc.write(m_base +addr, val);
endtask // write
virtual task read32( uint32_t addr, output uint32_t val );
uint64_t val64;
m_acc.read(m_base +addr, val64);
val = val64;
endtask // write
endclass // BusDevice
class WBUartDriver extends IBusDevice;
function new(CBusAccessor bus, uint64_t base);
super.new(bus, base);
endfunction // new
protected bit m_with_fifo;
protected byte m_tx_queue[$];
protected byte m_rx_queue[$];
protected bit m_tx_idle;
function automatic uint32_t calc_baudrate( uint64_t baudrate, uint64_t base_clock);
return ( ((( baudrate << 12)) + (base_clock >> 8)) / (base_clock >> 7) );
endfunction
task automatic init( uint32_t baudrate, uint32_t clock_freq, int fifo_en );
uint32_t rv;
read32( `ADDR_UART_SR, rv );
write32(`ADDR_UART_BCR, calc_baudrate( baudrate, clock_freq) );
if(!fifo_en)
m_with_fifo = 0;
else
m_with_fifo = (rv & `UART_SR_RX_FIFO_SUPPORTED) ? 1 : 0;
m_tx_idle = 0;
$display("wb_simple_uart: FIFO supported = %d", m_with_fifo);
endtask // init
task automatic send( byte value );
m_tx_queue.push_back(value);
m_tx_idle = 0;
update();
endtask // send
function automatic byte recv();
if( rx_count() == 0 )
return -1;
return m_rx_queue.pop_front();
endfunction // recv
function automatic int rx_count();
return m_rx_queue.size();
endfunction // rx_count
function automatic bit poll();
return m_rx_queue.size() > 0;
endfunction // has_data
function automatic bit tx_idle();
return m_tx_idle;
endfunction // tx_idle
function automatic bit rx_overflow();
endfunction // rx_overflow
task automatic update();
automatic uint32_t sr;
automatic time ts = $time;
read32( `ADDR_UART_SR, sr );
if( m_with_fifo ) begin
if( sr & `UART_SR_RX_RDY ) begin
automatic uint32_t d;
read32(`ADDR_UART_RDR, d);
// $display("FifoRx: %x", d);
m_rx_queue.push_back(d);
end
if( ! ( sr & `UART_SR_TX_FIFO_FULL ) && m_tx_queue.size() > 0 ) begin
byte d = m_tx_queue.pop_front();
// $display("-> FifoTX %x", d);
write32(`ADDR_UART_TDR, d);
end else if ( !m_tx_queue.size() ) begin
m_tx_idle = 1;
end
end else begin
if( ! ( sr & `UART_SR_TX_BUSY ) && m_tx_queue.size() > 0) begin
byte d = m_tx_queue.pop_front();
// $display("NoFifoTX");
write32(`ADDR_UART_TDR, d);
end else if ( !m_tx_queue.size() ) begin
m_tx_idle = 1;
end
if( sr & `UART_SR_RX_RDY ) begin
automatic uint32_t d;
read32(`ADDR_UART_RDR, d);
// $display("NoFifoRx: %x", d);
m_rx_queue.push_back(d);
end
end
endtask // update
endclass // WBUartDriver
module main;
reg rst_n = 0;
reg clk_62m5 = 0;
always #8ns clk_62m5 <= ~clk_62m5;
initial begin
repeat(20) @(posedge clk_62m5);
rst_n = 1;
end
// the Device Under Test
xwb_simple_uart
#(
.g_WITH_PHYSICAL_UART(1'b1),
.g_WITH_PHYSICAL_UART_FIFO(1'b1),
.g_TX_FIFO_SIZE(64),
.g_RX_FIFO_SIZE(64),
.g_INTERFACE_MODE(PIPELINED),
.g_ADDRESS_GRANULARITY(0)
)
DUT_FIFO
(
.rst_n_i(rst_n),
.clk_sys_i (clk_62m5),
.slave_i (Host1.out),
.slave_o (Host1.in),
.uart_txd_o(txd),
.uart_rxd_i(rxd)
);
// the Device Under Test
xwb_simple_uart
#(
.g_WITH_PHYSICAL_UART(1'b1),
.g_WITH_PHYSICAL_UART_FIFO(1'b0),
.g_INTERFACE_MODE(PIPELINED),
.g_ADDRESS_GRANULARITY(0)
)
DUT_NO_FIFO
(
.rst_n_i(rst_n),
.clk_sys_i (clk_62m5),
.slave_i (Host2.out),
.slave_o (Host2.in),
.uart_txd_o(rxd),
.uart_rxd_i(txd)
);
IVHDWishboneMaster Host1
(
.clk_i (clk_62m5),
.rst_n_i (rst_n));
IVHDWishboneMaster Host2
(
.clk_i (clk_62m5),
.rst_n_i (rst_n));
initial begin
real t;
automatic CWishboneAccessor acc1 = Host1.get_accessor();
automatic WBUartDriver drv_fifo = new( acc1, 0 );
automatic CWishboneAccessor acc2 = Host2.get_accessor();
automatic WBUartDriver drv_no_fifo = new( acc2, 0 );
automatic int i;
acc1.set_mode(PIPELINED);
acc2.set_mode(PIPELINED);
#100ns;
// $stop;
@(posedge rst_n);
@(posedge clk_62m5);
drv_fifo.init(9216000, 62500000, 0);
drv_no_fifo.init(9216000, 62500000, 0);
#1us;
for(i=0;i<100;i++)
begin
drv_no_fifo.send(i);
drv_fifo.send(i);
drv_no_fifo.update();
drv_fifo.update();
end
forever
begin
// $display("%d %d", drv_fifo.tx_idle(), drv_no_fifo.tx_idle() );
drv_fifo.update();
drv_no_fifo.update();
if( drv_fifo.tx_idle() && drv_no_fifo.tx_idle() )
break;
end
$display("TX Complete");
for(i=0;i<500;i++)
begin
drv_fifo.update();
drv_no_fifo.update();
end
$display("TX Idle!");
for(i=0;i<100;i++)
begin
automatic int rx = drv_no_fifo.recv();
if( rx != i )
$error("NoFifo err %x vs %x", i, rx );
rx = drv_fifo.recv();
if( rx != i )
$error("Fifo err %x vs %x", i, rx );
end
$display("Test complete");
$stop;
end // initial begin
endmodule // main
#vlog -sv main.sv +incdir+. +incdir+../../include/wb +incdir+../include/vme64x_bfm +incdir+../../include +incdir+../include +incdir+../../sim
set StdArithNoWarnings 1
set NumericStdNoWarnings 1
vsim -L unisim -L XilinxCoreLib work.main -voptargs=+acc -t 10fs
set StdArithNoWarnings 1
set NumericStdNoWarnings 1
do wave.do