Commit 6cb4c9ee authored by Maciej Lipinski's avatar Maciej Lipinski

Merge branch 'dio_extension' into 'master'

Dio extension

See merge request !4
parents 31e53528 1c190a5f
......@@ -53,3 +53,5 @@ modules.order
# Project Specifi #
###################
wr-dio.mod*
sw/irq-demo/irq-demo
sw/irq-demo/*.log
\ No newline at end of file
*~
*.aux
*.cp
*.cps
*.fn
*.html
*.info
*.ky
*.log
*.pdf
*.pg
*.texi
*.toc
*.tp
/*.txt
*.vr
#
# Makefile for the documentation directory
#
# Copyright 1994,2000,2010,2011 Alessandro Rubini <rubini@linux.it>
#
#################
# There is not basenames here, all *.in are considered input
INPUT = $(wildcard *.in)
TEXI = $(INPUT:.in=.texi)
INFO = $(INPUT:.in=.info)
HTML = $(INPUT:.in=.html)
TXT = $(INPUT:.in=.txt)
PDF = $(INPUT:.in=.pdf)
ALL = $(INFO) $(HTML) $(TXT) $(PDF)
MAKEINFO ?= makeinfo
RELEASE=$(shell git describe --always --dirty)
%.texi: %.in
@rm -f $@
sed s/__RELEASE_GIT_ID__/$(RELEASE)/ $< | sed -f ./infofilter > $@
emacs -batch --no-site-file -l fixinfo $@
chmod -w $@
%.pdf: %.texi
texi2pdf --batch $<
%.info: %.texi
$(MAKEINFO) $< -o $@
%.html: %.texi
$(MAKEINFO) --html --no-split -o $@ $<
%.txt: %.texi
$(MAKEINFO) --no-headers $< > $@
##############################################
.PHONY: all images check terse clean install
.INTERMEDIATE: $(TEXI)
all: images $(ALL)
$(MAKE) terse
images::
if [ -d images ]; then $(MAKE) -C images || exit 1; fi
check: _err.ps
gs -sDEVICE=linux -r320x200x16 $<
terse:
for n in cp fn ky pg toc tp vr aux log; do rm -f *.$$n; done
rm -f *~
clean: terse
rm -f $(ALL) $(TEXI)
install:
# add the other unused targets, so the rule in ../Makefile works
modules modules_install:
;; use:
;; emacs -batch -l ./fixinfo.el <file>
;; or, better:
;; emacs -batch --no-site-file -l ./fixinfo.el <file>
(defun fixinfo (file)
(find-file-other-window file)
(message (concat "Maxing texinfo tree in " file))
(texinfo-all-menus-update)
(texinfo-every-node-update)
(save-buffer)
(kill-buffer (current-buffer))
)
;; loop over command line arguments
(mapcar 'fixinfo command-line-args-left)
(kill-emacs)
This diff is collapsed.
#! /usr/bin/sed -f
# allow "%" as a comment char, but only at the beginning of the line
s/^%/@c /
#s/[^\\]%.*$//
s/^\\%/%/
#preserve blanks and braces in @example blocks
/^@example/,/^@end example/ s/{/@{/g
/^@example/,/^@end example/ s/}/@}/g
/^@example/,/^@end example/ p
/^@example/,/^@end example/ d
/^@smallexample/,/^@end smallexample/ s/{/@{/g
/^@smallexample/,/^@end smallexample/ s/}/@}/g
/^@smallexample/,/^@end smallexample/ p
/^@smallexample/,/^@end smallexample/ d
# remove leading blanks
s/^[ ]*//
......@@ -3,6 +3,7 @@ files = ["wr_dio_wb.vhd",
"wr_dio.vhd",
"wr_dio_pkg.vhd",
"pulse_gen_pl.vhd",
"immed_pulse_counter.vhd",
"dummy_time.vhd" ]
"immed_pulse_counter.vhd",
"dummy_time.vhd",
"imm_pulse_train_gen.vhd" ]
-------------------------------------------------------------------------------
-- Title : Immediate pulse train generator
-- Project : White Rabbit Network Interface
-------------------------------------------------------------------------------
-- File : imm_pulse_train_gen.vhd
-- Author : Jorge Machado
-- Company : Seven Solutions
-- Created : 2020-03-19
-- Last update:
-- Platform : FPGA-generic
-- Standard : VHDL
-------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
use IEEE.NUMERIC_STD.ALL;
-- Uncomment the following library declaration if instantiating
-- any Xilinx primitives in this code.
--library UNISIM;
--use UNISIM.VComponents.all;
entity imm_pulse_train_gen is
generic(
pulse_period_width : integer := 28
);
port(
clk_ref_i : in std_logic;
rst_n_i : in std_logic;
dio_pulse_immed_stb_i : in std_logic;
pulse_period_i : in std_logic_vector(pulse_period_width-1 downto 0);
pulse_output_o : out std_logic
);
end imm_pulse_train_gen;
architecture Behavioral of imm_pulse_train_gen is
signal nozeroperiod, nozeroperiod_aux : boolean;
signal pulse_period : std_logic_vector(pulse_period_width-1 downto 0);
signal dio_pulse_immed_stb_d0, dio_pulse_immed_stb_d1,
dio_pulse_immed_stb_d2, dio_pulse_immed_stb_d3 : std_logic;
-- Internal registers to hold pulse duration
signal counter : unsigned (pulse_period_width-1 downto 0);
-- Signals for states
type counter_state is (WAIT_ST, COUNTING, CAPTURE_PERIOD, TRIGGER);
signal state : counter_state;
signal repeat_pulse : std_logic;
-- Aux
constant zeros : std_logic_vector(pulse_period_width-1 downto 0) := (others => '0');
constant initial_pulse_delay_compensation : integer := 7;
constant repeat_pulse_delay_compensation : integer := 4;
begin
synchronization : process(clk_ref_i, rst_n_i)
begin
if (rst_n_i = '0') then
dio_pulse_immed_stb_d0 <= '0';
dio_pulse_immed_stb_d1 <= '0';
dio_pulse_immed_stb_d2 <= '0';
dio_pulse_immed_stb_d3 <= '0';
elsif rising_edge(clk_ref_i) then
dio_pulse_immed_stb_d0 <= dio_pulse_immed_stb_i;
dio_pulse_immed_stb_d1 <= dio_pulse_immed_stb_d0;
dio_pulse_immed_stb_d2 <= dio_pulse_immed_stb_d1;
dio_pulse_immed_stb_d3 <= dio_pulse_immed_stb_d2;
--pulse_period <= pulse_period_i;
nozeroperiod_aux <= pulse_period_i /= zeros;
if ((dio_pulse_immed_stb_d2 = '1' and dio_pulse_immed_stb_d1 = '0') or (state = CAPTURE_PERIOD)) then
nozeroperiod <= nozeroperiod_aux;
pulse_period <= pulse_period_i;
end if;
end if;
end process;
state_process : process(clk_ref_i, rst_n_i)
begin
if (rst_n_i = '0') then
counter <= (others => '0');
state <= WAIT_ST;
repeat_pulse <= '0';
elsif rising_edge(clk_ref_i) then
case state is
when WAIT_ST =>
if dio_pulse_immed_stb_d3 = '1' and nozeroperiod then
state <= COUNTING;
--Store the period two cycle before than the immed_pulse_counter_process to not lost one cycle.
counter <= unsigned(pulse_period)-initial_pulse_delay_compensation;
elsif repeat_pulse = '1' and nozeroperiod then
state <= COUNTING;
--Store the period four cycles before
counter <= unsigned(pulse_period)-repeat_pulse_delay_compensation;
else
state <= WAIT_ST;
end if;
when COUNTING =>
if counter = 0 then
state <= CAPTURE_PERIOD;
elsif dio_pulse_immed_stb_d2 = '1' then
state <= WAIT_ST;
else
state <= COUNTING;
counter <= counter-1;
end if;
when CAPTURE_PERIOD =>
state <= TRIGGER;
when TRIGGER =>
state <= WAIT_ST;
if(nozeroperiod) then
repeat_pulse <= '1';
else
repeat_pulse <= '0';
end if;
when others =>
state <= WAIT_ST;
end case;
end if;
end process;
output_process : process(rst_n_i, state)
begin
if (rst_n_i = '0') then
pulse_output_o <= '0';
else
case state is
when WAIT_ST =>
pulse_output_o <= '0';
when COUNTING =>
pulse_output_o <= '0';
when TRIGGER =>
pulse_output_o <= '1';
when others =>
pulse_output_o <= '0';
end case;
end if;
end process;
end Behavioral;
......@@ -66,6 +66,8 @@ architecture rtl of immed_pulse_counter is
-- Aux
constant zeros : std_logic_vector(pulse_length_width-1 downto 0) := (others => '0');
signal pulse_length : std_logic_vector(pulse_length_width-1 downto 0);
begin -- architecture rtl
synchronization : process(clk_i, rst_n_i)
......@@ -81,8 +83,10 @@ begin -- architecture rtl
pulse_start_d2 <= pulse_start_d1;
pulse_start_d3 <= pulse_start_d2;
nozerolength_aux <= pulse_length_i /= zeros;
--pulse_length <= pulse_length_i;
if (pulse_start_d2 = '1' and pulse_start_d1 = '0') then
nozerolength <= nozerolength_aux;
nozerolength <= nozerolength_aux;
pulse_length <= pulse_length_i;
end if;
end if;
end process;
......@@ -97,7 +101,7 @@ begin -- architecture rtl
when WAIT_ST =>
if pulse_start_d3 = '1' and nozerolength then
state <= COUNTING;
counter <= unsigned(pulse_length_i)-1;
counter <= unsigned(pulse_length)-1;
else
state <= WAIT_ST;
end if;
......@@ -114,7 +118,7 @@ begin -- architecture rtl
end if;
end process;
output_process : process(counter, state)
output_process : process(rst_n_i, state)
begin
if (rst_n_i = '0') then
pulse_output_o <= '0';
......
......@@ -76,8 +76,9 @@ entity pulse_gen_pl is
trig_utc_i : in std_logic_vector(39 downto 0);
trig_cycles_i : in std_logic_vector(27 downto 0);
trig_valid_p1_i : in std_logic;
pulse_length_i : in std_logic_vector(27 downto 0)
);
pulse_length_i : in std_logic_vector(27 downto 0);
pulse_period_i : in std_logic_vector(27 downto 0)
);
end pulse_gen_pl;
......@@ -87,7 +88,7 @@ architecture rtl of pulse_gen_pl is
signal trig_utc, trig_utc_ref : std_logic_vector(39 downto 0);
signal trig_cycles, trig_cycles_ref : std_logic_vector(27 downto 0);
signal pulse_length, pulse_length_ref : std_logic_vector(27 downto 0);
signal pulse_period, pulse_period_ref : std_logic_vector(27 downto 0);
-- Signals for the synchronizer
signal trig_valid_sys_d1, trig_valid_sys_d2 : std_logic;
signal rst_from_sync, rst_from_sync_d1 : std_logic;
......@@ -97,8 +98,11 @@ architecture rtl of pulse_gen_pl is
-- Aux
constant zeros : std_logic_vector(27 downto 0) := (others => '0');
signal counter : unsigned (27 downto 0);
signal nozerolength : boolean;
signal counter, train_counter : unsigned (27 downto 0);
signal nozerolength, nozeroperiod : boolean;
signal pulse_o_internal : std_logic;
signal pulse_train_trigger : std_logic;
signal load_values : std_logic;
begin -- architecture rtl
......@@ -110,10 +114,12 @@ begin -- architecture rtl
trig_utc <= (others => '0');
trig_cycles <= (others => '0');
pulse_length <= (others => '0');
pulse_period <= (others => '0');
elsif trig_valid_p1_i = '1' then
trig_utc <= trig_utc_i;
trig_cycles <= trig_cycles_i;
pulse_length <= pulse_length_i;
pulse_period <= pulse_period_i;
end if;
end if;
end process trig_regs;
......@@ -166,11 +172,13 @@ begin -- architecture rtl
trig_regs_ref : process (clk_ref_i)
begin
if clk_ref_i'event and clk_ref_i = '1' then
if trig_valid_ref_p1 = '1' then
if trig_valid_ref_p1 = '1' or load_values = '1' then
trig_utc_ref <= trig_utc;
trig_cycles_ref <= trig_cycles;
pulse_length_ref <= pulse_length;
pulse_period_ref <= pulse_period;
pulse_length_ref <= pulse_length;
nozerolength <= pulse_length /= zeros;
nozeroperiod <= pulse_period /= zeros;
end if;
end if;
end process trig_regs_ref;
......@@ -192,6 +200,7 @@ begin -- architecture rtl
end if;
end process ready_for_trig;
-- Produce output
-- Note rst_n_i is used as an async reset because it comes from the
-- clk_sys_i domain. Not the most elegant but it ensures no glitches
......@@ -201,19 +210,37 @@ begin -- architecture rtl
gen_out : process (rst_n_i, clk_ref_i)
begin
if rst_n_i = '0' then
pulse_o <= '0';
pulse_o_internal <= '0';
elsif clk_ref_i'event and clk_ref_i = '1' then
if tm_time_valid_i = '0' then
pulse_o <= '0';
elsif tm_utc_i = trig_utc_ref and tm_cycles_i = trig_cycles_ref and nozerolength then
pulse_o <= '1';
pulse_o_internal <= '0';
elsif tm_utc_i = trig_utc_ref and tm_cycles_i = trig_cycles_ref and nozerolength then --Original trigger
pulse_o_internal <= '1';
counter <= unsigned(pulse_length_ref)-1;
elsif counter /= 0 then
train_counter <= unsigned(pulse_period_ref); --Store the value of the period
elsif counter /= 0 then --The period counter is reduced at the same time than the pulse counter
counter <= counter-1;
if(train_counter /= 0) then
train_counter <= train_counter-1;
end if;
-- elsif(train_counter = 10) then --Load new values for then next cycle
-- load_values <= '1';
elsif(train_counter = 1) then --Period trigger. Rearm both counters
pulse_o_internal <= '1';
counter <= unsigned(pulse_length_ref)-1;
train_counter <= unsigned(pulse_period_ref);
--load_values <= '1';
elsif train_counter > 1 then
train_counter <= train_counter-1;
pulse_o_internal <= '0';
load_values <= '0';
else
pulse_o <= '0';
pulse_o_internal <= '0';
load_values <= '0';
end if;
end if;
end process gen_out;
end architecture rtl;
pulse_o <= pulse_o_internal;
end architecture rtl;
\ No newline at end of file
This diff is collapsed.
......@@ -70,10 +70,10 @@ package wr_dio_pkg is
wbd_width => x"7", -- 8/16/32-bit port granularity
sdb_component => (
addr_first => x"0000000000000000",
addr_last => x"00000000000000ff",
addr_last => x"00000000000003ff",
product => (
vendor_id => x"00000000000075CB", -- SEVEN SOLUTIONS
device_id => x"00000001",
device_id => x"00000003",
version => x"00000002",
date => x"20120709",
name => "WR-DIO-Registers ")));
......
This diff is collapsed.
This diff is collapsed.
......@@ -216,8 +216,13 @@ entity dio_common_top is
-- I2C interface for accessing FMC EEPROM. Deprecated, was used in
-- pre-v3.0 releases to store WRPC configuration. Now we use Flash for this.
dio_scl_b : inout std_logic;
dio_sda_b : inout std_logic
dio_sda_b : inout std_logic;
-- Leap second
leap_second_value_i : in std_logic_vector(15 downto 0);
leap_second_flag_59_i : in std_logic;
leap_second_flag_61_i : in std_logic;
leap_second_flag_valid_i : in std_logic
);
end entity dio_common_top;
......@@ -240,10 +245,10 @@ architecture top of dio_common_top is
dio_clk_i : in std_logic;
dio_pps_i : in std_logic;
dio_in_i : in std_logic_vector(4 downto 0);
dio_out_o : out std_logic_vector(4 downto 0);
dio_oe_n_o : out std_logic_vector(4 downto 0);
dio_term_en_o : out std_logic_vector(4 downto 0);
dio_in_i : in std_logic_vector(5 downto 0);
dio_out_o : out std_logic_vector(5 downto 0);
dio_oe_n_o : out std_logic_vector(5 downto 0);
dio_term_en_o : out std_logic_vector(5 downto 0);
dio_onewire_b : inout std_logic;
dio_sdn_n_o : out std_logic;
dio_sdn_ck_n_o : out std_logic;
......@@ -265,7 +270,13 @@ architecture top of dio_common_top is
slave_i : in t_wishbone_slave_in;
slave_o : out t_wishbone_slave_out;
dio_int : out std_logic
dio_int : out std_logic;
-- Leap second
leap_second_value_i : in std_logic_vector(15 downto 0);
leap_second_flag_59_i : in std_logic;
leap_second_flag_61_i : in std_logic;
leap_second_flag_valid_i : in std_logic
);
end component;
......@@ -325,8 +336,8 @@ architecture top of dio_common_top is
signal svec_led : std_logic_vector(15 downto 0);
-- DIO Mezzanine
signal dio_in : std_logic_vector(4 downto 0);
signal dio_out : std_logic_vector(4 downto 0);
signal dio_in : std_logic_vector(5 downto 0);
signal dio_out : std_logic_vector(5 downto 0);
-- Timecode output
signal tm_time_valid : std_logic;
......@@ -349,6 +360,9 @@ architecture top of dio_common_top is
-- VIC-only signals
signal vic_only_irqs : std_logic_vector(3 downto 0);
signal dio_oe_n_o_internal : std_logic_vector(5 downto 0);
signal dio_term_en_o_internal : std_logic_vector(5 downto 0);
begin -- architecture top
......@@ -663,8 +677,8 @@ begin -- architecture top
dio_pps_i => wrc_pps_out,
dio_in_i => dio_in,
dio_out_o => dio_out,
dio_oe_n_o => dio_oe_n_o,
dio_term_en_o => dio_term_en_o,
dio_oe_n_o => dio_oe_n_o_internal,
dio_term_en_o => dio_term_en_o_internal,
dio_onewire_b => dio_onewire_b,
dio_sdn_n_o => dio_sdn_n_o,
dio_sdn_ck_n_o => dio_sdn_ck_n_o,
......@@ -676,9 +690,16 @@ begin -- architecture top
tm_cycles_i => tm_cycles,
slave_i => cnx_slave_in(c_WB_SLAVE_DIO),
slave_o => cnx_slave_out(c_WB_SLAVE_DIO),
dio_int => dio_int
dio_int => dio_int,
leap_second_value_i => leap_second_value_i,
leap_second_flag_59_i => leap_second_flag_59_i,
leap_second_flag_61_i => leap_second_flag_61_i,
leap_second_flag_valid_i => leap_second_flag_valid_i
);
dio_oe_n_o <= dio_oe_n_o_internal(4 downto 0);
dio_term_en_o <= dio_term_en_o_internal(4 downto 0);
vic_vec_int(0) <= dio_int;
NIC_GEN : if g_dio_mode = DIO_NIC generate
......
......@@ -205,12 +205,16 @@ package dio_common_top_pkg is
dio_onewire_b : inout std_logic;
dio_sdn_n_o : out std_logic;
dio_sdn_ck_n_o : out std_logic;
-- I2C interface for accessing FMC EEPROM. Deprecated, was used in
-- pre-v3.0 releases to store WRPC configuration. Now we use Flash for this.
dio_scl_b : inout std_logic;
dio_sda_b : inout std_logic
dio_sda_b : inout std_logic;
-- Leap second
leap_second_value_i : in std_logic_vector(15 downto 0);
leap_second_flag_59_i : in std_logic;
leap_second_flag_61_i : in std_logic;
leap_second_flag_valid_i : in std_logic
);
end component;
......@@ -238,7 +242,7 @@ package dio_common_top_pkg is
constant c_nic_bridge_sdb : t_sdb_bridge :=
f_xwb_bridge_manual_sdb(x"0001ffff", x"00011000");
constant c_wr_dio_bridge_sdb : t_sdb_bridge :=
f_xwb_bridge_product_manual_sdb(x"00000fff", x"00000400", c_xwr_dio_sdb);
f_xwb_bridge_product_manual_sdb(x"00000fff", x"00000800", c_xwr_dio_sdb);
-- Primary wishbone crossbar layout (NIC)
constant c_NIC_WB_LAYOUT : t_sdb_record_array(c_NUM_WB_SLAVES - 1 downto 0) := (
......
......@@ -283,7 +283,11 @@ begin
dio_sdn_n_o => dio_sdn_n_o,
dio_sdn_ck_n_o => dio_sdn_ck_n_o,
dio_scl_b => dio_scl_b,
dio_sda_b => dio_sda_b
dio_sda_b => dio_sda_b,
leap_second_value_i => x"0025", -- leapSeoncs=37 in 2020
leap_second_flag_59_i => '0',
leap_second_flag_61_i => '0',
leap_second_flag_valid_i => '1'
);
end architecture top;
......@@ -15,6 +15,8 @@ export FMC_BUS_ABS
DIRS = $(FMC_BUS_ABS) kernel tools
CFLAGS += $(WR_DIO_CFLAGS)
.PHONY: all clean modules install modules_install $(DIRS)
all clean modules install modules_install: $(DIRS)
......
CFLAGS += -I./dep
irq-demo: irq-demo.c ./dep/*.c
gcc -o irq-demo irq-demo.c ./dep/*.c $(CFLAGS) -lm
clean:
-rm irq-demo
-rm .irq-demo.log
/*
* Copyright (C) 2020 CERN (www.cern.ch)
*
* Author: Miguel Jimenez Lopez <miguel.jimenez@sevensols.com>
*
* Released to the public domain as sample code to be customized.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
#ifndef __FILE_LOG_PRIVATE_H__
#define __FILE_LOG_PRIVATE_H__
struct file_log_private {
FILE *fp;
char *path;
};
static int check_file_log_priv(log_device dev);
static struct file_log_private * get_private_info(log_device dev);
static void init_file_log_device(log_device dev);
static void send_to_file_log_device(log_device, const char *msg);
static void deinit_file_log_device(log_device dev);
#endif
/*
* Copyright (C) 2020 CERN (www.cern.ch)
*
* Author: Miguel Jimenez Lopez <miguel.jimenez@sevensols.com>
*
* Released to the public domain as sample code to be customized.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
#include <stdlib.h>
#include <stddef.h>
#include <stdio.h>
#include "log-device.h"
#include "file-log-private.h"
static struct log_device_iface _file_log_iface =
{
.init_log_device = init_file_log_device,
.send_to_log_device = send_to_file_log_device,
.clear_log_device = NULL,
.deinit_log_device = deinit_file_log_device,
};
static const log_device_interface file_log_iface = &_file_log_iface;
log_device create_file_log_device(const char *path)
{
return create_log_device((void *)path, file_log_iface);
}
static void init_file_log_device(log_device dev)
{
struct file_log_private *priv;
priv = calloc(1, sizeof(*priv));
priv->path = dev->private;
priv->fp = fopen(priv->path, "w+");
dev->private = priv;
}
static void send_to_file_log_device(log_device dev, const char *msg)
{
struct file_log_private *priv;
if(!check_file_log_priv(dev)) {
priv = get_private_info(dev);
if(priv && priv->fp) {
fputs(msg, priv->fp);
fputs("\n", priv->fp);
}
}
}
static void deinit_file_log_device(log_device dev)
{
struct file_log_private *priv;
if(!check_file_log_priv(dev)) {
priv = get_private_info(dev);
if(priv) {
if(priv->fp) {
fclose(priv->fp);
}
free(priv);
}
}
}
static int check_file_log_priv(log_device dev)
{
return (dev->private != NULL) ? 0 : 1;
}
static struct file_log_private * get_private_info(log_device dev)
{
return (struct file_log_private *)dev->private;
}
/*
* Copyright (C) 2020 CERN (www.cern.ch)
*
* Author: Miguel Jimenez Lopez <miguel.jimenez@sevensols.com>
*
* Released to the public domain as sample code to be customized.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
#ifndef __FILE_LOG_H__
#define __FILE_LOG_H__
log_device create_file_log_device(const char *file);
#endif
/*
* Copyright (C) 2020 CERN (www.cern.ch)
*
* Author: Miguel Jimenez Lopez <miguel.jimenez@sevensols.com>
*
* Released to the public domain as sample code to be customized.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
#ifndef __FMC_DIO_DEVICE_PRIVATE_H__
#define __FMC_DIO_DEVICE_PRIVATE_H__
#include "usr-timestamp.h"
#include "../../kernel/fmc-dio.h"
#define MAX_TMP_BUF 1024
#define FMC_DIO_N_CH 5
#define NS_IN_A_SEC 1000000000
#define MAX_FMC_DIO_PATH 100
struct fmc_dio_dev {
char dev_name[MAX_FMC_DIO_PATH];
int fd;
log_device *logs;
unsigned int n_logs;
int enabled_log;
unsigned int total_n_ts;
unsigned int partial_n_ts;
struct wr_dio_cmd cmd;
};
static const fmc_dio_device BAD_FMC_DEVICE = NULL;
static int check_fmc_dio_device(fmc_dio_device dev);
static int enable_fmc_dio_device_hw_irq(fmc_dio_device dev,
int ch, int en);
static int setup_fmc_dio_device_hw_irq(fmc_dio_device dev,
unsigned long period,
long count);
static int get_state_fmc_dio_device_hw_irq(fmc_dio_device dev,
int ch, int * state);
static int get_hw_ts_from_fmc_dio_device(fmc_dio_device dev, int ch,
struct usr_timestamp **ts, unsigned int *nts, int flags);
static int get_kernel_leaps_info(fmc_dio_device dev);
static void log_msg(fmc_dio_device dev, const char *msg);
static void LOG(fmc_dio_device dev, const char *fmt, ...);
#endif
/*
* Copyright (C) 2020 CERN (www.cern.ch)
*
* Author: Miguel Jimenez Lopez <miguel.jimenez@sevensols.com>
*
* Released to the public domain as sample code to be customized.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/timex.h>
#include <sys/ioctl.h>
#include "fmc-dio-device.h"
#include "fmc-dio-device-private.h"
#define FMC_DIO_FLUSH_SLEEP 1
fmc_dio_device create_fmc_dio_device(char *name)
{
fmc_dio_device dev = calloc(1, sizeof(*dev));
if(!dev)
return BAD_FMC_DEVICE;
strncpy(dev->dev_name, name, MAX_FMC_DIO_PATH-1);
return dev;
}
static int check_fmc_dio_device(fmc_dio_device dev)
{
if(dev != BAD_FMC_DEVICE) {
return 0;
}
else {
return 1;
}
}
int open_fmc_dio_device(fmc_dio_device dev)
{
if(check_fmc_dio_device(dev))
return -EINVAL;
dev->fd = open(dev->dev_name, O_RDWR);
if(dev->fd < 0) {
free(dev);
return -ENODEV;
}
if(ioctl(dev->fd, PRIV_MEZZANINE_ID, (unsigned long) dev->dev_name) < 0 &&
errno != EAGAIN) {
free(dev);
return -EFAULT;
}
return 0;
}
void attach_log_devices_to_fmc_dio_device(fmc_dio_device dev,
log_device *log_devs,
unsigned int n_log_devs)
{
if(check_fmc_dio_device(dev) || !log_devs || n_log_devs == 0)
return;
dev->logs = log_devs;
dev->n_logs = n_log_devs;
}
int enable_log_for_fmc_dio_device(fmc_dio_device dev)
{
if(check_fmc_dio_device(dev))
return -EINVAL;
dev->enabled_log = 1;
return 0;
}
int disable_log_for_fmc_dio_device(fmc_dio_device dev)
{
if(check_fmc_dio_device(dev))
return -EINVAL;
dev->enabled_log = 0;
return 0;
}
void close_fmc_dio_device(fmc_dio_device dev)
{
if(check_fmc_dio_device(dev))
return;
close(dev->fd);
free(dev);
}
int enable_fmc_dio_device_irq(fmc_dio_device dev, int ch)
{
int ret;
if(check_fmc_dio_device(dev)) {
return -EINVAL;
}
ret = enable_fmc_dio_device_hw_irq(dev, ch, 1);
return ret;
}
int enable_fmc_dio_device_all_irq(fmc_dio_device dev)
{
int ret = 0;
for(int i = 0 ; i <= FMC_DIO_N_CH ; i++) {
ret = enable_fmc_dio_device_irq(dev, i);
if(ret)
break;
}
return ret;
}
int disable_fmc_dio_device_irq(fmc_dio_device dev, int ch)
{
int ret;
if(check_fmc_dio_device(dev)) {
return -EINVAL;
}
ret = enable_fmc_dio_device_hw_irq(dev, ch, 0);
return ret;
}
int disable_fmc_dio_device_all_irq(fmc_dio_device dev)
{
int ret = 0;
for(int i = 0 ; i <= FMC_DIO_N_CH ; i++) {
ret = disable_fmc_dio_device_irq(dev, i);
if(ret)
break;
}
return ret;
}
static int enable_fmc_dio_device_hw_irq(fmc_dio_device dev,
int ch, int en)
{
if(ch < 0 || ch > FMC_DIO_N_CH) {
return -EINVAL;
}
struct wr_dio_cmd *c = &dev->cmd;
en = (en) ? 1 : 0;
c->command = WR_DIO_CMD_MASK_IRQ;
c->flags = WR_DIO_F_MASK;
c->channel = 1 << ch;
c->value = (en) ? WR_DIO_F_MASK_ENABLE_IRQ :
WR_DIO_F_MASK_DISABLE_IRQ;
if(ioctl(dev->fd, PRIV_MEZZANINE_CMD, c) < 0)
return -EFAULT;
return 0;
}
int setup_fmc_dio_device_irq(fmc_dio_device dev,
unsigned long period,
long count)
{
int ret;
if(check_fmc_dio_device(dev))
return -EINVAL;
ret = setup_fmc_dio_device_hw_irq(dev, period, count);
return ret;
}
static int setup_fmc_dio_device_hw_irq(fmc_dio_device dev,
unsigned long period,
long count)
{
struct wr_dio_cmd *c = &dev->cmd;
int ret;
c->command = WR_DIO_CMD_IRQ;
c->flags = 0x0;
c->flags |= WR_DIO_F_LOOP;
c->flags |= WR_DIO_F_NOW;
c->t[1].tv_nsec = period % NS_IN_A_SEC;
c->t[1].tv_sec = period / NS_IN_A_SEC;
c->value= count;
ret = ioctl(dev->fd, PRIV_MEZZANINE_CMD, c);
if(ret < 0) {
return -EFAULT;
}
return 0;
}
int get_fmc_dio_device_all_irq_state(fmc_dio_device dev, int * state)
{
int ret = 0, state_internal = 0, channel_state = 0;
for(int i = 0 ; i <= FMC_DIO_N_CH ; i++) {
ret = get_fmc_dio_device_irq_state(dev, i, &channel_state);
if(ret)
break;
state_internal |= (channel_state & 0x1) << i;
}
*state = state_internal;
return ret;
}
int get_fmc_dio_device_irq_state(fmc_dio_device dev, int ch, int * state)
{
int ret;
if(check_fmc_dio_device(dev)) {
return -EINVAL;
}
ret = get_fmc_dio_device_hw_irq_state(dev, ch, state);
return ret;
}
int get_fmc_dio_device_hw_irq_state(fmc_dio_device dev, int ch, int * state)
{
if(ch < 0 || ch > FMC_DIO_N_CH) {
return -EINVAL;
}
struct wr_dio_cmd *c = &dev->cmd;
c->command = WR_DIO_CMD_MASK_IRQ;
c->flags = WR_DIO_F_MASK;
c->channel = 1 << ch;
c->value = WR_DIO_F_MASK_READ_IRQ;
if(ioctl(dev->fd, PRIV_MEZZANINE_CMD, c) < 0)
return -EFAULT;
if(c->value == WR_DIO_F_MASK_ENABLE_IRQ)
*state = 1;
else if(c->value == WR_DIO_F_MASK_DISABLE_IRQ)
*state = 0;
else
return -EFAULT;
return 0;
}
int set_fmc_dio_device_all_irq_state(fmc_dio_device dev, int state)
{
int ret = 0;
for(int i = 0 ; i <= FMC_DIO_N_CH ; i++) {
ret = enable_fmc_dio_device_hw_irq(dev, i, (state >> i) & 0x1);
if(ret)
break;
}
return ret;
}
int get_tai_ts_from_fmc_dio_device(fmc_dio_device dev, int ch,
struct usr_timestamp **ts, unsigned int *nts)
{
if(check_fmc_dio_device(dev))
return -EINVAL;
return get_hw_ts_from_fmc_dio_device(dev, ch, ts, nts, WR_DIO_F_WAIT);
}
int get_utc_ts_from_fmc_dio_device(fmc_dio_device dev, int ch,
struct usr_timestamp **ts, unsigned int *nts)
{
int ret;
int kernel_tai;
int usr_tai;
int usr_tai_valid;
int tai;
struct timespec *t;
ret = get_tai_ts_from_fmc_dio_device(dev, ch, ts, nts);
if(ret)
return ret;
kernel_tai = get_kernel_leaps_info(dev);
for(int i = 0 ; i < *nts ; i++) {
t = &((*ts)[i].t);
usr_tai = (*ts)[i].leap_second;
#if defined(IGNORE_USR_LEAPSECOND)
usr_tai_valid = 0;
#else
usr_tai_valid = (*ts)[i].leap_second_valid;
#endif
tai = (usr_tai_valid) ? usr_tai : kernel_tai;
t->tv_sec -= tai;
}
return ret;
}
static int get_hw_ts_from_fmc_dio_device(fmc_dio_device dev, int ch,
struct usr_timestamp **ts, unsigned int *nts, int flags)
{
struct wr_dio_cmd *c = &dev->cmd;
int ret;
c->command = WR_DIO_CMD_STAMP;
c->flags = flags;
c->channel = ch;
ret = ioctl(dev->fd, PRIV_MEZZANINE_CMD, (unsigned long) c);
if(ret < 0) {
return ret;
}
if(c->nstamp <= 0) {
return -EAGAIN;
}
*nts = c->nstamp;
*ts = calloc(c->nstamp, sizeof(struct usr_timestamp));
if(!*ts)
return -ENOMEM;
for(int i = 0 ; i < c->nstamp ; i++) {
(*ts)[i].t = (c->t[i]);
(*ts)[i].leap_second = (c->wr_ts_info_buf[i]).leap_second;
(*ts)[i].leap_second_valid = (c->wr_ts_info_buf[i]).leap_second_valid;
(*ts)[i].flag59 = (c->wr_ts_info_buf[i]).flag59;
(*ts)[i].flag61 = (c->wr_ts_info_buf[i]).flag61;
}
dev->total_n_ts += c->nstamp;
dev->partial_n_ts += c->nstamp;
return 0;
}
void flush_fmc_dio_device_channel(fmc_dio_device dev, int ch)
{
int ret;
struct usr_timestamp *ts;
unsigned int nts;
LOG(dev, "Flushing FMC DIO device, channel %d... ", ch);
sleep(FMC_DIO_FLUSH_SLEEP);
ret = get_hw_ts_from_fmc_dio_device(dev, ch, &ts, &nts, 0);
if(ret == 0)
free(ts);
LOG(dev, "Ok!\n");
}
static int get_kernel_leaps_info(fmc_dio_device dev)
{
struct timex tx = {0};
if (adjtimex(&tx) < 0) {
return -EFAULT;
}
return tx.tai;
}
unsigned int get_total_n_timestamps(fmc_dio_device dev)
{
if(check_fmc_dio_device(dev))
return 0;
return dev->total_n_ts;
}
unsigned int get_partial_n_timestamps(fmc_dio_device dev)
{
if(check_fmc_dio_device(dev))
return 0;
return dev->partial_n_ts;
}
void reset_partial_n_timestamps(fmc_dio_device dev)
{
if(check_fmc_dio_device(dev))
return;
dev->partial_n_ts = 0;
}
static void log_msg(fmc_dio_device dev, const char *msg)
{
if(!dev->logs || dev->n_logs == 0)
return;
if(!dev->enabled_log)
return;
for(int i = 0 ; i < dev->n_logs ; i++)
send_to_log_device(dev->logs[i], msg);
}
static void LOG(fmc_dio_device dev, const char *fmt, ...)
{
va_list ap;
char buffer[MAX_TMP_BUF] = {0};
va_start(ap, fmt);
vsnprintf(buffer, MAX_TMP_BUF-1, fmt, ap);
va_end(ap);
log_msg(dev, buffer);
}
/*
* Copyright (C) 2020 CERN (www.cern.ch)
*
* Author: Miguel Jimenez Lopez <miguel.jimenez@sevensols.com>
*
* Released to the public domain as sample code to be customized.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
#ifndef __FMC_DIO_DEVICE_H__
#define __FMC_DIO_DEVICE_H__
#include "log-device.h"
#include "usr-timestamp.h"
typedef struct fmc_dio_dev * fmc_dio_device;
fmc_dio_device create_fmc_dio_device(char *name);
int open_fmc_dio_device(fmc_dio_device dev);
void close_fmc_dio_device(fmc_dio_device dev);
void attach_log_devices_to_fmc_dio_device(fmc_dio_device dev,
log_device *log_devs,
unsigned int n_log_devs);
int enable_log_for_fmc_dio_device(fmc_dio_device dev);
int disable_log_for_fmc_dio_device(fmc_dio_device dev);
int enable_fmc_dio_device_irq(fmc_dio_device dev, int ch);
int enable_fmc_dio_device_all_irq(fmc_dio_device dev);
int disable_fmc_dio_device_irq(fmc_dio_device dev, int ch);
int disable_fmc_dio_device_all_irq(fmc_dio_device dev);
int setup_fmc_dio_device_irq(fmc_dio_device dev,
unsigned long period,
long count);
int get_fmc_dio_device_all_irq_state(fmc_dio_device dev, int * state);
int get_fmc_dio_device_irq_state(fmc_dio_device dev, int ch, int * state);
int get_fmc_dio_device_hw_irq_state(fmc_dio_device dev, int ch, int * state);
int set_fmc_dio_device_all_irq_state(fmc_dio_device dev, int state);
int get_tai_ts_from_fmc_dio_device(fmc_dio_device dev, int ch,
struct usr_timestamp **ts, unsigned int *nts);
int get_utc_ts_from_fmc_dio_device(fmc_dio_device dev, int ch,
struct usr_timestamp **ts, unsigned int *nts);
void flush_fmc_dio_device_channel(fmc_dio_device dev, int ch);
unsigned int get_total_n_timestamps(fmc_dio_device dev);
unsigned int get_partial_n_timestamps(fmc_dio_device dev);
void reset_partial_n_timestamps(fmc_dio_device dev);
#endif
/*
* Copyright (C) 2020 CERN (www.cern.ch)
*
* Author: Miguel Jimenez Lopez <miguel.jimenez@sevensols.com>
*
* Released to the public domain as sample code to be customized.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
#ifndef __LOG_DEVICE_PRIVATE_H__
#define __LOG_DEVICE_PRIVATE_H__
static const log_device BAD_LOG_DEVICE = NULL;
static int check_log_device(log_device dev);
static int check_log_device_interface(log_device dev);
#endif
/*
* Copyright (C) 2020 CERN (www.cern.ch)
*
* Author: Miguel Jimenez Lopez <miguel.jimenez@sevensols.com>
*
* Released to the public domain as sample code to be customized.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
#include <stdlib.h>
#include "log-device.h"
#include "log-device-private.h"
log_device create_log_device(void *args, log_device_interface interface)
{
log_device dev = calloc(1, sizeof(struct log_dev));
if(dev != BAD_LOG_DEVICE && interface != NULL) {
dev->private = args;
dev->ops = interface;
if(dev->ops->init_log_device)
dev->ops->init_log_device(dev);
}
return dev;
}
void send_to_log_device(log_device dev, const char *msg)
{
if(check_log_device_interface(dev))
return;
dev->ops->send_to_log_device(dev, msg);
}
void clear_log_device(log_device dev)
{
if(check_log_device_interface(dev))
return;
if(dev->ops->clear_log_device)
dev->ops->clear_log_device(dev);
}
void destroy_log_device(log_device dev)
{
if(check_log_device_interface(dev))
return;
if(dev->ops->deinit_log_device)
dev->ops->deinit_log_device(dev);
free(dev);
}
static int check_log_device(log_device dev)
{
return (dev == BAD_LOG_DEVICE) ? 1 : 0;
}
static int check_log_device_interface(log_device dev)
{
if(check_log_device(dev))
return 1;
if(!dev->ops)
return 1;
else
return 0;
}
/*
* Copyright (C) 2020 CERN (www.cern.ch)
*
* Author: Miguel Jimenez Lopez <miguel.jimenez@sevensols.com>
*
* Released to the public domain as sample code to be customized.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
#ifndef __LOG_DEVICE_H__
#define __LOG_DEVICE_H__
typedef struct log_device_iface * log_device_interface;
struct log_dev {
log_device_interface ops;
void *private;
};
typedef struct log_dev * log_device;
struct log_device_iface {
void (*init_log_device)(log_device dev);
void (*send_to_log_device)(log_device dev, const char *msg);
void (*deinit_log_device)(log_device dev);
void (*clear_log_device)(log_device dev);
};
log_device create_log_device(void *args, log_device_interface interface);
void send_to_log_device(log_device dev, const char *msg);
void clear_log_device(log_device dev);
void destroy_log_device(log_device dev);
#endif
/*
* Copyright (C) 2020 CERN (www.cern.ch)
*
* Author: Miguel Jimenez Lopez <miguel.jimenez@sevensols.com>
*
* Released to the public domain as sample code to be customized.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
#ifndef __PRINTF_LOG_PRIVATE_H__
#define __PRINTF_LOG_PRIVATE_H__
static void send_to_printf_log_device(log_device dev, const char *msg);
static void clear_printf_log_device(log_device dev);
#endif
/*
* Copyright (C) 2020 CERN (www.cern.ch)
*
* Author: Miguel Jimenez Lopez <miguel.jimenez@sevensols.com>
*
* Released to the public domain as sample code to be customized.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
#include <stddef.h>
#include <stdio.h>
#include "log-device.h"
#include "printf-log-private.h"
static struct log_device_iface _printf_log_iface =
{
.init_log_device = NULL,
.send_to_log_device = send_to_printf_log_device,
.deinit_log_device = NULL,
.clear_log_device = clear_printf_log_device,
};
static const log_device_interface printf_log_iface = &_printf_log_iface;
static void send_to_printf_log_device(log_device dev, const char *msg)
{
printf("%s\n", msg);
}
static void clear_printf_log_device(log_device dev)
{
printf("\e[1;1H\e[2J");
}
log_device create_printf_log_device(void)
{
return create_log_device(NULL, printf_log_iface);
}
/*
* Copyright (C) 2020 CERN (www.cern.ch)
*
* Author: Miguel Jimenez Lopez <miguel.jimenez@sevensols.com>
*
* Released to the public domain as sample code to be customized.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
#ifndef __PRINTF_LOG_H__
#define __PRINTF_LOG_H__
#include "log-device.h"
log_device create_printf_log_device(void);
void clear_screen(log_device device);
#endif
/*
* Copyright (C) 2020 CERN (www.cern.ch)
*
* Author: Miguel Jimenez Lopez <miguel.jimenez@sevensols.com>
*
* Released to the public domain as sample code to be customized.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
#ifndef __STATS_ENGINE_PRIVATE_H__
#define __STATS_ENGINE_PRIVATE_H__
#include "usr-timestamp.h"
#define MAX_TS 1000
#define NS_IN_A_SEC 1000000000
#define MAX_TMP_BUF 1024
#define METRICS_UNIT "s"
struct ts_stats {
double mean;
double std;
double min;
double max;
double peak_to_peak;
unsigned int ts_per_second;
unsigned int ts_total;
};
struct _stats_engine {
struct usr_timestamp usr_ts[MAX_TS];
struct timespec sys_ts[MAX_TS];
double diff_ts[MAX_TS];
struct ts_stats stats;
unsigned int n_ts;
unsigned int current_ts;
unsigned int next_ts;
unsigned int n_iter;
log_device *logs;
unsigned int n_logs;
int enabled_log;
};
const stats_engine BAD_STATS_ENGINE = NULL;
static void update_n_ts_stats_engine(stats_engine engine);
static void update_index_stats_engine(unsigned int *index);
static void log_stats_engine(stats_engine engine, int verbose);
static int check_log_configuration(stats_engine engine);
static void log_msg(stats_engine engine,
const char *msg);
static void log_clear(stats_engine engine);
static void LOG(stats_engine engine, const char *fmt, ...);
static int timespec_subtract(struct timespec *result,
struct timespec *x,
struct timespec *y);
static double compute_min(const double *values,
const unsigned int n_values);
static double compute_max(const double *values,
const unsigned int n_values);
static double compute_peak_to_peak(const double min,
const double max);
static double compute_mean(const double *values,
const unsigned int n_values);
static double compute_stdev(const double *values,
const unsigned int n_values,
const double mean);
static int compute_ts_per_second(const int sec,
const struct usr_timestamp *ts,
const unsigned int n_ts);
#endif
/*
* Copyright (C) 2020 CERN (www.cern.ch)
*
* Author: Miguel Jimenez Lopez <miguel.jimenez@sevensols.com>
*
* Released to the public domain as sample code to be customized.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <stdarg.h>
#include <errno.h>
#include "stats-engine.h"
#include "stats-engine-private.h"
stats_engine create_stats_engine(void)
{
stats_engine engine = calloc(1, sizeof(*engine));
return engine;
}
int check_stats_engine(stats_engine engine)
{
if(engine != BAD_STATS_ENGINE)
return 0;
else
return 1;
}
void attach_log_devices_to_stats_engine(stats_engine engine,
log_device *log_devs,
unsigned int n_log_devs)
{
if(check_stats_engine(engine) || !log_devs || n_log_devs == 0)
return;
engine->logs = log_devs;
engine->n_logs = n_log_devs;
}
int enable_log_for_stats_engine(stats_engine engine)
{
if(check_stats_engine(engine))
return -EINVAL;
engine->enabled_log = 1;
return 0;
}
int disable_log_for_stats_engine(stats_engine engine)
{
if(check_stats_engine(engine))
return -EINVAL;
return 0;
}
static struct timespec ts_t0 = {0};
static int neg_t0=0;
int add_usr_timestamp_to_stats_engine(stats_engine engine, struct usr_timestamp *ts,clock_type sysclkt)
{
struct timespec system_ts;
if(check_stats_engine(engine))
return -EINVAL;
if(sysclkt==CLOCK_REALTIME)
{
clock_gettime(CLOCK_REALTIME, &system_ts);
}
else
{
clock_gettime(CLOCK_MONOTONIC, &system_ts);
if(ts_t0.tv_sec==0) //Set starting point t0 of monotonic time
{
neg_t0=timespec_subtract(&ts_t0, &(ts->t),&system_ts);
printf("ts_0=%ld . %ld\n",ts_t0.tv_sec,ts_t0.tv_nsec);
}
if(neg_t0) timespec_subtract(&(ts->t), &(ts_t0),&(ts->t));
else timespec_subtract(&(ts->t), &(ts->t),&(ts_t0));
}
engine->sys_ts[engine->next_ts] = system_ts;
engine->usr_ts[engine->next_ts] = *ts;
update_index_stats_engine(&engine->next_ts);
update_n_ts_stats_engine(engine);
return 0;
}
int run_stats_engine(stats_engine engine, int verbose)
{
struct timespec ts;
int neg;
int count_ts;
if(check_stats_engine(engine))
return -EINVAL;
neg = timespec_subtract(&ts, &engine->sys_ts[engine->current_ts],
&(engine->usr_ts[engine->current_ts].t));
engine->diff_ts[engine->current_ts] = ((neg) ? -1.0 : 1.0)*(ts.tv_sec + ((double) ts.tv_nsec / NS_IN_A_SEC));
engine->stats.min = compute_min(engine->diff_ts, engine->n_ts);
engine->stats.max = compute_max(engine->diff_ts, engine->n_ts);
engine->stats.mean = compute_mean(engine->diff_ts, engine->n_ts);
engine->stats.std = compute_stdev(engine->diff_ts, engine->n_ts, engine->stats.mean);
engine->stats.peak_to_peak = compute_peak_to_peak(engine->stats.min, engine->stats.max);
count_ts = compute_ts_per_second(engine->usr_ts[engine->current_ts].t.tv_sec,
engine->usr_ts, engine->n_ts);
if(count_ts != -1)
engine->stats.ts_per_second = count_ts;
engine->stats.ts_total++;
log_stats_engine(engine, verbose);
update_index_stats_engine(&engine->current_ts);
engine->n_iter++;
return 0;
}
void destroy_stats_engine(stats_engine engine)
{
if(check_stats_engine(engine))
return;
free(engine);
}
static void update_n_ts_stats_engine(stats_engine engine)
{
if(engine->n_ts < MAX_TS)
engine->n_ts++;
}
static void update_index_stats_engine(unsigned int *index)
{
if(*index == MAX_TS-1)
*index = 0;
else
(*index)++;
}
static void log_stats_engine(stats_engine engine, int verbose)
{
log_clear(engine);
LOG(engine, "\n===================== STATS ENGINE LOG =====================\n");
if(verbose) {
LOG(engine, "Timestamps in buffer: %d", engine->n_ts);
LOG(engine, "Next timestamp (index): %d\n", engine->next_ts);
for(int i = 0 ; i < engine->n_ts ; i++) {
LOG(engine, "\tsys_ts[%d]: sec %ld, nsec %ld",
i, engine->sys_ts[i].tv_sec,
engine->sys_ts[i].tv_nsec);
LOG(engine, "\tusr_ts[%d]: sec %ld, nsec %ld [ls_valid: %d, ls: %d, l59: %d, l61: %d]",
i, engine->usr_ts[i].t.tv_sec,
engine->usr_ts[i].t.tv_nsec,
engine->usr_ts[i].leap_second_valid,
engine->usr_ts[i].leap_second,
engine->usr_ts[i].flag59,
engine->usr_ts[i].flag61);
LOG(engine, "\tdiff[%d]: %f %s",
i, engine->diff_ts[i], METRICS_UNIT);
}
LOG(engine, "");
}
LOG(engine, "Iterations: %d", engine->n_iter);
LOG(engine, "Metrics => Total timestamps: %d, Timestamps per second: %d\n"
"max: %f %s, min %f %s, peak to peak %f %s\n"
"mean: %f %s, stdev: %f %s",
engine->stats.ts_total, engine->stats.ts_per_second,
engine->stats.max, METRICS_UNIT, engine->stats.min, METRICS_UNIT,
engine->stats.peak_to_peak, METRICS_UNIT,
engine->stats.mean, METRICS_UNIT, engine->stats.std, METRICS_UNIT);
LOG(engine, "\n============================================================\n");
LOG(engine, "Please, press 'q' key + ENTER or CNTRL+C to exit from the demo tool\n");
}
static int check_log_configuration(stats_engine engine)
{
if(!engine->logs || engine->n_logs == 0)
return 1;
if(!engine->enabled_log)
return 1;
return 0;
}
static void log_msg(stats_engine engine, const char *msg)
{
if(check_log_configuration(engine))
return;
for(int i = 0 ; i < engine->n_logs ; i++)
send_to_log_device(engine->logs[i], msg);
}
static void log_clear(stats_engine engine)
{
if(check_log_configuration(engine))
return;
for(int i = 0 ; i < engine->n_logs ; i++)
clear_log_device(engine->logs[i]);
}
static void LOG(stats_engine engine, const char *fmt, ...)
{
va_list ap;
char buffer[MAX_TMP_BUF] = {0};
va_start(ap, fmt);
vsnprintf(buffer, MAX_TMP_BUF-1, fmt, ap);
va_end(ap);
log_msg(engine, buffer);
}
static int timespec_subtract(struct timespec *result,
struct timespec *x,
struct timespec *y)
{
struct timespec xx = *x;
struct timespec yy = *y;
x = &xx; y = &yy;
if (x->tv_nsec > NS_IN_A_SEC-1)
{
x->tv_sec += x->tv_nsec / NS_IN_A_SEC;
x->tv_nsec %= NS_IN_A_SEC;
}
if (y->tv_nsec > NS_IN_A_SEC-1)
{
y->tv_sec += y->tv_nsec / NS_IN_A_SEC;
y->tv_nsec %= NS_IN_A_SEC;
}
result->tv_sec = x->tv_sec - y->tv_sec;
result->tv_nsec = x->tv_nsec - y->tv_nsec;
if(result->tv_sec>0 && result->tv_nsec < 0)
{
result->tv_nsec += NS_IN_A_SEC;
result->tv_sec--;
}
else if(result->tv_sec<0 && result->tv_nsec > 0)
{
result->tv_nsec -= NS_IN_A_SEC;
result->tv_sec++;
}
return (result->tv_sec < 0) || (result->tv_nsec<0);
}
static double compute_min(const double *values,
const unsigned int n_values)
{
double min = 0.0;
if(n_values == 0)
return min;
min = values[0];
for(int i = 1 ; i < n_values ; i++) {
if(values[i] < min)
min = values[i];
}
return min;
}
static double compute_peak_to_peak(const double min, const double max)
{
return max-min;
}
static double compute_max(const double *values,
const unsigned int n_values)
{
double max = 0.0;
if(n_values == 0)
return max;
max = values[0];
for(int i = 1 ; i < n_values ; i++) {
if(values[i] > max)
max = values[i];
}
return max;
}
static double compute_mean(const double *values,
const unsigned int n_values)
{
double mean = 0.0;
for(int i = 0 ; i < n_values ; i++) {
mean += values[i];
}
mean /= n_values;
return mean;
}
static double compute_stdev(const double *values,
const unsigned int n_values,
const double mean)
{
double stdev = 0.0;
double aux = 0.0;
for(int i = 0 ; i < n_values ; i++) {
aux += pow((values[i]-mean),2);
}
stdev = sqrt((aux/n_values));
return stdev;
}
static int compute_ts_per_second(const int sec,
const struct usr_timestamp *ts,
const unsigned int n_ts)
{
static int prev_sec = -1;
int count = -1;
if(prev_sec == -1) {
prev_sec = sec;
}
else {
if(prev_sec != sec) {
count = 0;
for(int i = 0 ; i < n_ts ; i++) {
if(ts[i].t.tv_sec == prev_sec)
count++;
}
prev_sec = sec;
}
}
return count;
}
/*
* Copyright (C) 2020 CERN (www.cern.ch)
*
* Author: Miguel Jimenez Lopez <miguel.jimenez@sevensols.com>
*
* Released to the public domain as sample code to be customized.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
#ifndef __STATS_ENGINE_H__
#define __STATS_ENGINE_H__
#include "log-device.h"
#include "usr-timestamp.h"
typedef enum{
REALTIME=0,
MONOTONIC,
} clock_type;
typedef struct _stats_engine *stats_engine;
stats_engine create_stats_engine(void);
int check_stats_engine(stats_engine engine);
void attach_log_devices_to_stats_engine(stats_engine engine,
log_device *log_devs,
unsigned int n_log_devs);
int enable_log_for_stats_engine(stats_engine engine);
int disable_log_for_stats_engine(stats_engine engine);
int add_usr_timestamp_to_stats_engine(stats_engine engine,
struct usr_timestamp *ts,clock_type sysclkt);
int run_stats_engine(stats_engine engine, int verbose);
void destroy_stats_engine(stats_engine engine);
#endif
#ifndef __USR_TIMESTAMP_H__
#define __USR_TIMESTAMP_H__
#include <time.h>
#include <stdint.h>
struct usr_timestamp {
struct timespec t;
uint16_t leap_second;
int leap_second_valid;
int flag59;
int flag61;
};
#endif
/*
* Copyright (C) 2020 CERN (www.cern.ch)
*
* Author: Miguel Jimenez Lopez <miguel.jimenez@sevensols.com>
*
* Released to the public domain as sample code to be customized.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
#ifndef __DEMO_IRQ_PRIVATE_H__
#define __DEMO_IRQ_PRIVATE_H__
#include "usr-timestamp.h"
#include "stats-engine.h"
#define PROG_NAME "irq-demo"
#define TEST_FMC_DIO_CH 5
#define TEST_FMC_DIO_COUNT -1
#define TEST_FMC_DIO_PERIOD 100000000
#define STATS_LOG_PATH "./.irq-demo.log"
#define MAX_FMC_DIO_PATH 100
struct _user_args {
char fmc_dev_path[MAX_FMC_DIO_PATH];
unsigned int irq_period;
int verbose;
clock_type clock;
log_device err_log;
};
typedef struct _user_args * user_args;
static void demo_irq_process_loop(fmc_dio_device fmc_dev, stats_engine engine);
static void process_timestamps_to_engine(stats_engine engine, struct usr_timestamp *ts,
unsigned int nts, int last_only);
static user_args create_user_arguments(log_device log);
static int parse_user_arguments(int argc, char *argv[], user_args parsed_args);
static void destroy_user_arguments(user_args args);
static void show_help(void);
static int check_unknown_user_arguments(int optind, int argc, char **argv);
static unsigned int check_user_stop(void);
static void set_stdin_as_nonblocking(void);
static void exit_abruptly(int sig);
#endif
This diff is collapsed.
......@@ -20,7 +20,10 @@ ccflags-y += -DGIT_VERSION=\"$(GIT_VERSION)\"
obj-m += wr-dio.o
wr-dio-y = fmc-dio.o
wr-dio-y = ./hw/wr-dio-regs.o
wr-dio-y += ./hw/wr-dio-regs_v1.o
wr-dio-y += ./hw/wr-dio-regs_v2.o
wr-dio-y += fmc-dio.o
wr-dio-y += fmc-dio-internal.o
wr-dio-y += fmc-dio-mdev.o
wr-dio-y += fmc-dio-gpio.o
\ No newline at end of file
This diff is collapsed.
......@@ -55,7 +55,7 @@ static int fmc_dio_resource_map(struct fmc_dio *dio)
{
int i,err;
struct platform_device *dev = dio->pdev;
struct fmc_device *fmc = dio->fmc;
//struct fmc_device *fmc = dio->fmc;
struct resource *r;
void __iomem *mem;
......@@ -170,7 +170,19 @@ static int fmc_dio_pl_probe(struct platform_device *dev)
// Specific board info
dio->board = dev->id_entry->driver_data;
dio->pdev = dev;
dio->version = dio->board;
if(dio->version == FMC_DIO_BOARD_SPEC) {
printk("DIO driver. Detected DIO V1");
}
else if(dio->version == FMC_DIOv2_BOARD_SPEC) {
printk("DIO driver. Detected DIO V2");
}
else{
printk("DIO driver. DIO not detected");
}
/* Map the resources for the FMC DIO */
ret = fmc_dio_resource_map(dio);
if(ret)
......@@ -239,6 +251,10 @@ static const struct platform_device_id fmc_dio_id_table[] = {
.name = "fmc-dio-spec",
.driver_data = FMC_DIO_BOARD_SPEC,
},
{ /* SPEC compatible */
.name = "fmc-diov2-spec",
.driver_data = FMC_DIOv2_BOARD_SPEC,
},
{},
};
......
......@@ -52,6 +52,7 @@ struct fmc_dio_gpio_block {
/* Board constants */
#define FMC_DIO_BOARD_SPEC 0
#define FMC_DIOv2_BOARD_SPEC 1
/**
* @brief FMC DIO structure
......@@ -74,6 +75,8 @@ struct fmc_dio {
int board; /**< Board info */
int version; //*< HDL version */
void *priv; /**< Private data for FMC DIO */
};
......@@ -104,6 +107,9 @@ enum wr_dio_cmd_name {
WR_DIO_CMD_STAMP,
WR_DIO_CMD_DAC,
WR_DIO_CMD_INOUT,
WR_DIO_CMD_IRQ,
WR_DIO_CMD_UPDATE_WIDTH,
WR_DIO_CMD_MASK_IRQ,
};
/*
......@@ -141,13 +147,23 @@ enum wr_dio_cmd_name {
#define WR_DIO_N_STAMP 16 /* At least 5 * 3 */
struct wr_timestamp_info {
uint16_t leap_second;
int leap_second_valid;
int flag59;
int flag61;
};
struct wr_dio_cmd {
uint16_t command; /* from user */
uint16_t channel; /* 0..4 or mask from user */
uint32_t value; /* for DAC or I/O */
uint32_t flags;
uint32_t nstamp; /* from kernel, if IN_STAMP */
struct timespec t[WR_DIO_N_STAMP]; /* may be from user */
struct timespec t[WR_DIO_N_STAMP];
struct wr_timestamp_info wr_ts_info_buf[WR_DIO_N_STAMP];
uint32_t wait_mode_flags;
struct timespec wait_mode_timeout;
};
#define WR_DIO_F_NOW 0x01 /* Output is now, t[0] ignored */
......@@ -155,3 +171,8 @@ struct wr_dio_cmd {
#define WR_DIO_F_MASK 0x04 /* Channel is 0x00..0x1f */
#define WR_DIO_F_LOOP 0x08 /* Output should loop: t[2] is looping*/
#define WR_DIO_F_WAIT 0x10 /* Wait for event */
#define WR_DIO_F_MASK_READ_IRQ 0x20
#define WR_DIO_F_MASK_ENABLE_IRQ 0x40
#define WR_DIO_F_MASK_DISABLE_IRQ 0x80
#define WR_DIO_F_WAIT_MODE_NORM 0x00
#define WR_DIO_F_WAIT_MODE_TIMEOUT 0x01
#include "wr-dio-regs.h"
struct regmap *get_regmap(unsigned int ver)
{
if(ver == 0)
return regmap_v1;
else
return regmap_v2;
}
struct regmap_common get_regmap_common(unsigned int ver)
{
if(ver == 0)
return regmap_common_v1;
else
return regmap_common_v2;
}
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment