Commit b95e05fb authored by Tomasz Wlostowski's avatar Tomasz Wlostowski

Merge remote-tracking branch 'fdsw/develop' into tom-spec-convention

parents 44b4ee21 54eafb43
*.o
*.ko
*~
*.mod.c
/.tmp_versions
.*cmd
modules.order
Module.symvers
Makefile.specific
*.tmp
GTAGS
GPATH
GRTAGS
[submodule "zio"]
path = zio
url = git://ohwr.org/misc/zio.git
==========
Change Log
==========
[3.0.0] - 2019-10-17
====================
Functionalists are mainly the same as before, but the architecture has changed
and some loading mechanism as well, for this reason we decided to increase
the major version.
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
# include parent_common.mk for buildsystem's defines
# use absolute path for REPO_PARENT
REPO_PARENT ?= $(shell /bin/pwd)/..
-include $(REPO_PARENT)/parent_common.mk
all: kernel lib tools
# The user can override, using environment variables, all these three:
ZIO ?= zio
# ZIO_ABS has to be absolut path, due to beeing
# passed to the Kbuild
ZIO_ABS ?= $(abspath $(ZIO) )
export ZIO_ABS
ZIO_VERSION = $(shell cd $(ZIO_ABS); git describe --always --dirty --long --tags)
export ZIO_VERSION
DIRS = $(ZIO_ABS) kernel lib tools
$(SPEC_SW_ABS):
kernel: $(ZIO_ABS)
lib: $(ZIO_ABS)
tools: lib
DESTDIR ?= /usr/local
.PHONY: all clean modules install modules_install $(DIRS)
.PHONY: gitmodules prereq_install prereq_install_warn
install modules_install: prereq_install_warn
all clean modules install modules_install: $(DIRS)
clean: TARGET = clean
modules: TARGET = modules
install: TARGET = install
modules_install: TARGET = modules_install
$(DIRS):
$(MAKE) -C $@ $(TARGET)
SUBMOD = $(ZIO_ABS)
prereq_install_warn:
@test -f .prereq_installed || \
echo -e "\n\n\tWARNING: Consider \"make prereq_install\"\n"
prereq_install:
for d in $(SUBMOD); do $(MAKE) -C $$d modules_install || exit 1; done
touch .prereq_installed
$(ZIO_ABS): zio-init_repo
# init submodule if missing
zio-init_repo:
@test -d $(ZIO_ABS)/doc || ( echo "Checking out submodule $(ZIO_ABS)" && git submodule update --init $(ZIO_ABS) )
include scripts/gateware.mk
Please check doc/
In ./doc/, fine-delay.in is the source file, and you can "make" to get
pdf and other formats provided you have the proper tools installed
(mainly texinfo and tex).
*~
*.aux
*.cp
*.cps
*.fn
*.html
*.info
*.ky
*.log
fine-delay.pdf
*.pg
*.texi
*.toc
*.tp
/*.txt
*.vr
_build
.doxystamp
doxygen-fd-output
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = .
BUILDDIR = _build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
$(MAKE) -C drawings
$(MAKE) doxygen TARGET=$@
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
GIT_VERSION = $(shell git describe --dirty --long --tags)
GIT_VERSION = $(shell git describe --dirty --long --tags)
doxygen:
ifeq ($(TARGET),clean)
@rm -rf doxygen-fd-output .doxystamp
else
$(MAKE) .doxystamp
endif
# List of Doxygen folders to consider
DOXINPUT := ../../lib
DOXINPUT += ../../kernel/fine-delay.h
DOXEXCL := ../../lib/PyFmcFineDelay
# List of actual Doxygen source files
DOXSRC = $(shell find $(DOXINPUT) -type f -name '*.[chS]')
.doxystamp: $(DOXSRC)
GIT_VERSION=$(GIT_VERSION) DOXINPUT="$(DOXINPUT)" DOXEXCL="$(DOXEXCL)" doxygen ./doxygen-fd-config
@touch .doxystamp
# Configuration file for the Sphinx documentation builder.
#
# This file only contains a selection of the most common options. For a full
# list see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
# -- Path setup --------------------------------------------------------------
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))
# -- Project information -----------------------------------------------------
project = 'Fmc Fine-Delay Software'
copyright = '2020, Alessandro Rubini'
author = 'Alessandro Rubini <rubini@gnudd.com>, Tomasz Wlostowski <Tomasz.Wlostowski@cern.ch>'
# -- General configuration ---------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'breathe',
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The master toctree document.
master_doc = 'index'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
# -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'sphinx_rtd_theme'
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
breathe_projects = {
"fine-delay": "doxygen-fd-output/xml/",
}
breathe_default_project = "fine-delay"
==========================
Information for Developers
==========================
This appendix contains some extra information on the internals of the driver
and how to access it bypassing the library. If you are not a *fine-delay*,
*fmc-bus* or *zio* developer, you can skip this chapter and just use
the library.
Source Code Conventions
=======================
This is a random list of conventions I use in this package
* All internal symbols in the driver begin with ``fd_``
(excluding local variables like *i* and similar stuff). So you know
if something is local or comes from the kernel.
* All library functions and public data begin with ``fdelay_``.
* The board passed as a library token (``struct fdelay_board``)
is opaque, so the user doesn't access it. Internally it is called
``userb`` because ``b`` is the real one being used. If you need
to access library internals from a user file just define
``FDELAY_INTERNAL`` before including ``fdelay-lib.h``.
* The driver header is called ``fine-delay.h`` while the user one
is ``fdelay-lib.h``. The latter includes the former, which user
programs should not refer to.
* The *tools* directory hosts the suggested command-line tools
to use the device for testing and quick access. They demonstrate use
of the library functions using internally-consistent command line
conventions. All command names begin with ``fmc-fdelay-``
* The *oldtools* directory includes tools that access *zio*
directly; they are not built by default any more because they are now
deprecated; we also removed documentation for them, for the same
reason. We keep them for our previous users, in case they still want
to run previous scripts the saved in the past. The directory
also includes tools that used to be built withing ``lib/`` and
are deprecated as well. The old tools use
the name patterns ``fd-raw-`` and ``fdelay-``
* The *lib* directory contains the userspace library, providing a
simple C API to access the driver.
* The *tools* directory contains a number of tools built on top of that
library that let you access all features of the *fine-delay* mezzanine.
Using the driver directly
=========================
The driver is designed as a *zio* driver that offers 1 input channel and
4 output channels. Since each output channel is independent (they do
not output at the same time) the device is modeled as 5 separate
*csets*.
The reader of this chapter is expected to be confident with basic *zio*
concepts, available in *zio* documentation (*zio* is an http://ohwr.org.
project).
The device
==========
The overall device includes a few device attributes and a few attributes
specific to the csets (some attributes for input and some attributes for
output).
The attributes allow to read and write the internal timing of the
card, as well as other internal parameters, documented below. Since *zio*
has no support for *ioctl*, all the attributes appear in *sysfs*.
For multi-valued attributes (like a time tag, which is more than 32
bits) the order of reading and writing is mandated by the driver
(e.g.: writing the seconds field of a time must be last, as it is the
action that fires hardware access for all current values).
The device appears in */dev* as a set of char devices:::
spusa# ls -l /dev/zio/*
crw------- 1 root root 249, 0 Apr 26 00:26 /dev/zio/fd-0200-0-0-ctrl
crw------- 1 root root 249, 1 Apr 26 00:26 /dev/zio/fd-0200-0-0-data
crw------- 1 root root 249, 32 Apr 26 00:26 /dev/zio/fd-0200-1-0-ctrl
crw------- 1 root root 249, 33 Apr 26 00:26 /dev/zio/fd-0200-1-0-data
crw------- 1 root root 249, 64 Apr 26 00:26 /dev/zio/fd-0200-2-0-ctrl
crw------- 1 root root 249, 65 Apr 26 00:26 /dev/zio/fd-0200-2-0-data
crw------- 1 root root 249, 96 Apr 26 00:26 /dev/zio/fd-0200-3-0-ctrl
crw------- 1 root root 249, 97 Apr 26 00:26 /dev/zio/fd-0200-3-0-data
crw------- 1 root root 249, 128 Apr 26 00:26 /dev/zio/fd-0200-4-0-ctrl
crw------- 1 root root 249, 129 Apr 26 00:26 /dev/zio/fd-0200-4-0-data
The actual pathnames depend on the version of *udev*, and the support
library tries the three names that have been used over time
(the newest name is shown above; the oldest didn't have the two
*zio*).
Also, please note that a still-newer version of *udev* obeys device
permissions, so you'll have read-only and write-only device files.
In this drivers, *cset* 0 is for the input signal, and *csets* 1..4 are
for the output channels.
If more than one board is probed for, you'll have two or more similar
sets of devices, differing in the ``dev_id`` field, i.e. the
``0200`` that follows the device name ``zio-fd`` in the
stanza above. The *dev_id* field is built using the PCI bus
and the *devfn* octet; the example above refers to slot 0 of bus 2.
For remotely-controlled devices (e.g. Etherbone) the problem will need
to be solved differently.
Device (and channel) attributes can be accessed in the proper *sysfs*
directory. For a card in slot 0 of bus 2 (like shown above), the
directory is ``/sys/bus/zio/devices/fd-0200``:::
spusa# ls -Ff /sys/bus/zio/devices/fd-0200/
./ cset0/ utc-h fd-ch2@ temperature
../ cset1/ utc-l fd-ch3@ version
name cset2/ resolution-bits fd-ch4@ uevent
command cset3/ coarse enable fd-input@
devname cset4/ driver@ calibration_data
devtype power/ fd-ch1@ subsystem@
Device Attributes
-----------------
Device-wide attributes are the three time tags (*utc-h*, *utc-l*,
*coarse*), a read-only *version*, a read-only *temperature*
and a write-only *command*.
To read device time you should read *utc-h* first. Reading *utc-h* will
atomically read all values from the card and store them in the software
driver: when reading *utc-l* and *coarse* you'll get such cached values.
Example:::
spusa# cd /sys/bus/zio/devices/fd-0200/
spusa# cat coarse coarse utc-h coarse
75136756
75136756
0
47088910
To set the time, you can write the three values leaving *utc-h*
last: writing *utc-h* atomically programs the hardware:::
spusa# echo 10000 > coarse; echo 10000 > utc-l; echo 0 > utc-h
spusa# cat utc-h utc-l
0
10003
The temperature value is scaled by four bits, so you need divide it by
16 to obtain the value in degrees. In this example:::
spusa# cat temperature
1129
Temperature is 70.5625 degrees.
If you write 0 to *command*, board time will be
synchronized to the current Linux clock within one microsecond
(reading Linux time and writing to the *fine-delay* registers is
done with interrupts disabled, so the actual synchronization precision
depends on the speed of your CPU and PCI bus):::
spusa# cat utc-h utc-l; echo 0 > command; cat utc-h utc-l; date +%s
0
50005
0
1335948116
1335948116
However, please note that the times will diverge over time. Also, if
you are using White-Rabbit mode, host time is irrelevant to the board.
I chose to offer a *command* channel, which is opaque to the user,
because there are several commands that you may need to send to the
device, and we need to limit the number of attributes. The command numbers
are enumerated in ``fine-delay.h`` and described here below.
List of Commands to the Device
------------------------------
The following commands are currently supported for the ``command``
write-only file in *sysfs*:
0 = FD_CMD_HOST_TIME
Set board time equal to host time.
1 = FD_CMD_WR_ENABLE
Enable White-Rabbit mode.
2 = FD_CMD_WR_DISABLE
Disable White-Rabbit mode.
3 = FD_CMD_WR_QUERY
Tell the user the status of White-Rabbit mode. This is a hack, as
the return value is reported using error codes. Success means
White-Rabbit is synchronized. ``ENODEV`` means WR mode is not supported
or inactive, ``EAGAIN```` means it is not synchronized yet.
The error is returned to the *write* system call.
4 = FD_CMD_DUMP_MCP
Force dumping to log messages (using a plain *printk* the
GPIO registers in the MCP23S17 device (fixme: is it really needed).
5 = FD_CMD_PURGE_FIFO
Empty the input fifo and reset the sequence number.
The Input cset
==============
The input cset returns blocks with no data and timestamp information in the
control structure (the meta-information associated to data). Before
January 2014 the driver was suboptimal, but now those limitations are
gone and the driver uses the ``self-timed`` *zio* abstraction, which
allows it to push blocks to the buffer even if no process is yet reading.
Collecting event in empty blocks, with full meta-data description, brings
some overhead in the data flow, mainly for the marshalling of meta-data.
If you need to stamp pulse rates higher than 10kHz we advise you to
rely on the *raw_tdc* support, which on an average computer can
timestamp up to 100-150 kHz without data loss. This is described
in :ref:`Raw TDC<dev_raw_tdc>`. The internals of the input data flow are
described in :ref:`The Input Data Flow<dev_data_flow>`, that may help fine-tune
driver parameters to match your timestamping needs.
For normal *zio* blocks, with meta-data and no data, the hardware
timestamp and other information is returned as *channel
attributes*, which you can look at using *zio-dump* (part of the *zio*
package) or
*tools/fd-raw-input* which is part of this package.
Input Device Attributes
-----------------------
The attributes are all 32-bit unsigned values, and their meaning
is defined in *fine-delay.h* for libraries/applications to use them:::
enum fd_zattr_in_idx {
FD_ATTR_TDC_UTC_H,
FD_ATTR_TDC_UTC_L,
FD_ATTR_TDC_COARSE,
FD_ATTR_TDC_FRAC,
FD_ATTR_TDC_SEQ,
FD_ATTR_TDC_CHAN,
FD_ATTR_TDC_FLAGS,
FD_ATTR_TDC_OFFSET,
FD_ATTR_TDC_USER_OFF,
};
#define FD_TDCF_DISABLE_INPUT 1
#define FD_TDCF_DISABLE_TSTAMP 2
#define FD_TDCF_TERM_50 4
The attributes are also visible in */sys*, in the directory
describing the cset:
::
spusa# ls -Ff /sys/bus/zio/devices/zio-fd-0200/fd-input/
./ devname utc-l offset
../ devtype current_trigger uevent
seq chan0/ user-offset current_buffer
chan flags coarse direction
frac power/ enable
name utc-h trigger/
The timestamp-related values in this file reflect the last stamp that
has been enqueued to user space (this may be the next event to be
read by the actual reading process).
The *offset* attribute is the stamping offset, in picoseconds, for the
TDC channel. The hardware timestamper's time-base is shifted
backwards, so the driver adds this offset to the raw timestamps it
collects. Users should not change this value, that depends on how
hardware and HDL is designed.
The *user-offset* attribute, which defaults to 0 every time the
driver is loaded, is a signed value that users can write to represent a
number of picoseconds to be added (or subtracted, if negative)
to the hardware-reported stamps. This is used to account for delays
induced by cabling (range: -2ms to 2ms).
The *flags* attribute can be used to change three configuration
bits, defined by the respective macros. Please note that the default
at module load time is zero: some of the flags bits are inverted
over the hardware counterpart, but the ``DISABLE`` in flag names
is there to avoid potential errors.
Reading with zio-dump
---------------------
This is an example read sequence using *zio-dump*: data must be ignored
and only the first few extended attributes are meaningful. This can
be used to see low-level details, but please note
that the programs in ``tools/`` and ``lib/`` in this package are
in general a better choice to timestamp input pulses.::
spusa# zio-dump /dev/zio/fd-0200-0-0-*
Ctrl: version 0.5, trigger user, dev fd, cset 0, chan 0
Ctrl: seq 1, n 16, size 4, bits 32, flags 01000001 (little-endian)
Ctrl: stamp 1335737285.312696982 (0)
Device attributes:
[...]
Extended: 0x0000003f
0x0 0x30 0x640f20d 0x60a 0x0 0x0 0x0 0x0
[...]
Extended: 0x0000003f
0x0 0x40 0x454b747 0x1d3 0x1 0x0 0x0 0x0
[...]
Extended: 0x0000003f
0x0 0x47 0xf04c57 0x772 0x2 0x0 0x0 0x0
Reading with fd-raw-input
-------------------------
The *tools/fd-raw-input* program, part of this package, is a low-level
program to read input events. It reads
the control devices associated to *fine-delay* cards, ignoring the
data devices which are known to not return useful information.
The program can receive
file names on the command line, but reads all fine-delay devices by
default -- it looks for filenames in */dev* using *glob* patterns (also
called ``wildcards``).
This is an example run:
::
spusa# ./tools/fd-raw-input
/dev/zio/zio-fd-0200-0-0-ctrl: 00000000 0000001a 00b9be2b 00000bf2 00000000
/dev/zio/zio-fd-0200-0-0-ctrl: 00000000 0000001b 00e7f5c2 0000097d 00000001
/dev/zio/zio-fd-0200-0-0-ctrl: 00000000 0000001b 02c88901 00000035 00000002
/dev/zio/zio-fd-0200-0-0-ctrl: 00000000 0000001b 03e23c26 000006ce 00000003
The program offers a ``float`` mode, that reports floating point
time differences between two samples (this doesn't use the *frac* delay
value, though, but only the integer second and the coarse 8ns timer).
This is an example while listening to a software-generated 1kHz signal:
::
spusa# ./tools/fd-raw-input -f
/dev/zio/fd-0200-0-0-ctrl: 1825.903957552 (delta 0.001007848)
/dev/zio/fd-0200-0-0-ctrl: 1825.904971384 (delta 0.001013832)
/dev/zio/fd-0200-0-0-ctrl: 1825.905968648 (delta 0.000997264)
/dev/zio/fd-0200-0-0-ctrl: 1825.906980376 (delta 0.001011728)
/dev/zio/fd-0200-0-0-ctrl: 1825.907997128 (delta 0.001016752)
The tool reports lost events using the sequence number (attribute number 4).
This is an example using a software-generated burst with a 10us period:::
/dev/zio/fd-0200-0-0-ctrl: 1958.385815880 (delta 0.000010024)
/dev/zio/fd-0200-0-0-ctrl: 1958.385825832 (delta 0.000009952)
/dev/zio/fd-0200-0-0-ctrl: 1958.385835720 (delta 0.000009888)
/dev/zio/fd-0200-0-0-ctrl: LOST 2770 events
/dev/zio/fd-0200-0-0-ctrl: 1958.412775304 (delta 0.026939584)
/dev/zio/fd-0200-0-0-ctrl: 1958.412784808 (delta 0.000009504)
/dev/zio/fd-0200-0-0-ctrl: 1958.412794808 (delta 0.000010000)
/dev/zio/fd-0200-0-0-ctrl: 1958.412804184 (delta 0.000009376)
The ``pico`` mode of the program (command line argument ``-p``) is
used to get input timestamps with picosecond precision. In this mode
the program doesn't report the ``second`` part of the stamp. This is
an example run of the program, fed by 1kHz generated from the board
itself:::
spusa.root# ./tools/fd-raw-input -p | head -5
/dev/zio/fd-0800-0-0-ctrl: 642705121635
/dev/zio/fd-0800-0-0-ctrl: 643705121647 - delta 001000000012
/dev/zio/fd-0800-0-0-ctrl: 644705121656 - delta 001000000009
/dev/zio/fd-0800-0-0-ctrl: 645705121647 - delta 000999999991
/dev/zio/fd-0800-0-0-ctrl: 646705121664 - delta 001000000017
If is possible, for diagnostics purposes, to run several modes
at the same time: while ``-f`` and ``-p`` disable raw/hex mode,
the equivalent options ``-r`` and ``-h`` reinstantiate it.
If the input event is reported in more than one format, the filename
is only printed once, and later lines begin with a single blank space
(you may see more blanks because they are part of normal output,
for alignment purposes).
If you are using the tool in a script, and you want to capture all the
samples in a burst and then terminate, you can specify a timeout, in
microseconds, using ``-t``. The timeout is only applied after the
first pulse is received.
Finally, the program uses two environment variables, if set to any value:
``FD_SHOW_TIME`` make the tool report the time difference between
sequential reads, which is mainly useful to debug the driver workings;
``FD_EXPECTED_RATE`` makes the tool report the difference from the
expected data rate, relative to the first sample collected:::
spusa.root# FD_EXPECTED_RATE=1000000000 ./tools/fd-raw-input -p | head -5
/dev/zio/fd-0800-0-0-ctrl: 139705121668
/dev/zio/fd-0800-0-0-ctrl: 140705121699 - delta 001000000031 - error 31
/dev/zio/fd-0800-0-0-ctrl: 141705121661 - delta 000999999962 - error -7
/dev/zio/fd-0800-0-0-ctrl: 142705121671 - delta 001000000010 - error 3
/dev/zio/fd-0800-0-0-ctrl: 143705121689 - delta 001000000018 - error 21
Please note that the expected rate is a 32-bit integer, so it is limited
to 4ms; moreover it is only used in ``picosecond`` mode.
Using fd-raw-perf
-----------------
The program *tools/fd-raw-perf* gives trivial performance figures for
a train of input pulses. It samples all input events and reports some
statistics when a burst completes (i.e., no pulse is received for at
least 300ms):::
spusa# ./tools/fd-raw-perf
59729 pulses (0 lost)
hw: 1000000000ps (1.000000kHz) -- min 999999926 max 1000000089 delta 163
sw: 983us (1.017294kHz) -- min 7 max 18992 delta 18985
The program uses the environment variable ``PERF_STEP``, if set, to
report information every that many seconds, even if the burst is still
running:::
spusa.root# PERF_STEP=5 ./tools/fd-raw-perf
4999 pulses (0 lost)
hw: 1000000000ps (1.000000kHz) -- min 999999933 max 1000000067 delta 134
sw: 1000us (1.000000kHz) -- min 8 max 10001 delta 9993
4999 pulses (0 lost)
hw: 1000000000ps (1.000000kHz) -- min 999999926 max 1000000081 delta 155
sw: 1000us (1.000000kHz) -- min 7 max 18995 delta 18988
Configuring the Input Channel
-----------------------------
There is no support in ``tools/`` to change channel configuration
(but see :ref:`Input Configuration<lib_input>` for the official API).
The user is expected to write values in the *flags* file directly.
For example, to enable the termination resistors, write 4 to the
*flags* file in *sysfs*.
Pulsing from the Parallel Port
------------------------------
For my initial tests, some of which are shown above, I generated bursts
of pulses with a software
program (later I used the board itself, for a much better precision).
To do so, I connected a pin of a parallel port plugged on the PCI bus to
the input channel of the *fine-delay* card.
The program *tools/parport-burst*, part of this package, generates a
burst according to three command line parameters: the I/O port of
the data byte of the parallel port, the repeat count and the duration
of each period. This example makes 1000 pulses of 100 usec each,
using the physical address of my parallel port (if yours is part
of the motherboard, the address is ``378``):::
./parport-burst d080 1000 100
.. _dev_raw_tdc:
Raw TDC
-------
If your rate of input pulses is above a dozen kHz, the overheader of
setting up a full *zio* block with proper control information may cause
some data loss; the actual threshold depends on the speed of your
computer and the amount of other activities that are going on.
By loading the module with the parameter ``raw_tdc=1``, you force
the input channel to carry timestamps in the data area; only the first
timestamp is properly converted to meta-data for the control
structure. This allow timestamping without data loss trains of pulses
of up to 150kHz; again, the actual limit depends on the performance of
your host computer and concurrent load.
Timestamps are returned as 24-byte-long data samples, i.e.
``struct fd_time``, as defined in the header file:::
struct fd_time {
uint64_t utc;
uint32_t coarse;
uint32_t frac;
uint32_t channel;
uint32_t seq_id;
};
For a simple pulse logging, the following shell command will work:::
insmod kernel/fmc-fine-delay.ko raw_tdc=1 fifo_len=16384
cat /dev/zio/fd-0200-0-0-data > logfile
Anders Walling provided tools for use with ``raw_tdc=1``. I'll try to
merge them with this package; meanwhile please find them in
https://github.com/aewallin/fine-delay-sw
.. _dev_data_flow:
The Input Data Flow
-------------------
This section described the input data flow, after a summary about
the basic *zio* concept, because most readers are not expected to be
confident with it.
Fdelay-sw implements a *zio* device. *zio* is a framework to
transport I/O data, its own atomic unit is a "block",
i.e. meta-information (*control*, or ``ctrl``) and actual samples
(*data*). Each block is like a network frame, in a way: header and
payload. The header/ctrl is 512 bytes and includes a very sharp
timestamp plus both standardized and device-specific "attribute"
values.
TDC/DTC devices are best represented as an empty block: the header
carries the timestamp and the attributes, and no data is associated
with the event. This however has an overhead: each timestamp is 512
bytes big, and is delivered as a separate object. With
``fd-raw-input`` I can collect 30-40kHz square waves, but not more
than that. This means my computer takes 25-30 microseconds per
sample, including the user-space overhead. This time is mainly taken
by the data conversion and attribute setting to provide high-level
information; the overhead of a *zio* block is less than one
microsecond, as documented elsewhere.
By using the new module parameter ``raw_tdc=1`` the data flow is
slightly modified and timestamps are delivered to user space in a much
lower-level format. The sample-size of the input channel is now 24
bytes (``struct fd_time``, defined in the header) and each block can
transport several samples in its data area. Thus, if configured for N
samples per block, *zio* allocates payload areas of ``24*N`` bytes;
when the input interrupt is served, the driver fills as many samples
as it can, up to N, it then stores the block to the *zio* buffer.
Thus, each block in the buffer will host 1 or more "raw" timestamps,
up to the configured value N. This lowers the computational load and
allows capturing fast bursts of many thousands pulses.
The data path is then split in the following steps
* In the gateware, timestamps are placed in a ring buffer (FIFO) that
is currently 1024-samples long (set by ``c_RING_BUFFER_SIZE_LOG2`` in
``fine_delay_pkg.vhd``).
* The irq handler pulls the hardware fifo and places samples into a
software ring buffer (fifo). The software fifo is an array of "struct
fd_time". Its size is configured by the insmod parameter
``fifo_len=`` (default is 1024 as I write this). The handler finally
sends acknowledgement to the hardware and awakes the software
interrupt.
* The software interrupt handler pulls the software fifo and fills the
already-allocated *zio* block, finally storing it to the buffer.
Both the block size and the number of blocks in the buffer are
configurable at run time. When *zio* allocates the next block, the
driver pulls the software fifo too, so any sample received in the
store-allocate interval is recovered in the new block. When using
``raw_tdc=1``, the *zio* control represents the first timestamp (so
consistency of the meta-information is preserved), and all stamps
including the first are included in the data area after a simple
normalization step. So the samples are not *very* raw, some
calculation is still performed, but much less than setting all
the *zio* attributes.
Thus, the critical points are the following ones:
* Hardware can timestamp up to its maximum speed (I tested 1MHz with no
issues) as long as the burst fits in the hw fifo.
* The irq handler moves the samples to the software fifo, while
splitting bit fields. Several samples are handled by each interrupt.
I think I can pull up to 300-500 kilosamples per second. But I didn't
prepare a specific test. This works with no loss as long as the
software fifo is not overflown. Clearly the sw fifo can be increased
at will: making it 64-ksample or more is not a problem, but the size
is constrained to be a power of two.
* Moving the samples from the software fifo to the *zio* buffer is
another step, which requires a little more data conversion
(normalization and addition of the user-defined constant offset).
There is a per-sample overhead and a (bigger) per-block overhead.
This step detects if an overflow of the software fifo happened. IF so,
it discards half of the fifo size to recover some margin.
The number of samples per *zio* block is configured by the "post-samples"
attribute (or pre-samples, which is usually left as 0 because stamps
are taken after the trigger event):::
echo 1000 > /sys/bus/zio/devices/fd-0200/fd-input/trigger/post-samples
A bigger size for the block means more wasted memory if pulses are
slow (the block is used almost-empty); a smaller size means more
overhead and thus a smaller maximum bursts frequency.
The buffer length (number of blocks), can be increased at will:::
echo 1000 > /sys/bus/zio/devices/fd-0200/fd-input/chan0/buffer/max-buffer-len
There is nothing against using a very long list of blocks in the
buffer, if user-space is slow in pulling data: blocks are only
allocated when needed. Federico recently added an attribute to
monitor buffer usage: ``allocated-buffer-len`` (which is always at
least 1, because one block is always ready to be filled by the next
interrupt).
Data can be read by user-space simply by reading::
/dev/zio/fd-0200-0-0-data
The file is a continuous stream of samples. Meta-information is
delivered to another device name: by reading data alone, the
application ignores the control structures that are properly released.
Each sample includes a 16-bit sequence number, so the final consumer
can detect overflows. This doesn't apply if the software fifo is 128k
samples, because samples are dropped half-a-fifosize each time --
maybe I can change this). If the *zio* buffer is overflown,
*zio* must discard one or more blocks. This is reported in the
*alarms* field of the control, also readable as ``alarms`` in sysfs. The
sysfs attribute is write-1-to-clear and there's no other way to
clear alarms.
In order to see how *zio* blocks flow, you can::
./zio/tools/zio-dump /dev/zio/fd-0200-0-0-*
or just *grep* the number of samples in each block, without even
reading the payload:::
./zio/tools/zio-dump /dev/zio/fd-0200-0-0-* | grep ", n "
You'll get something like this:::
Ctrl: seq 2257, n 26, size 24, bits 32, flags 01000001 (little-endian)
Ctrl: seq 2258, n 436, size 24, bits 32, flags 01000001 (little-endian)
Ctrl: seq 2259, n 2684, size 24, bits 32, flags 01000001 (little-endian)
Ctrl: seq 2260, n 4000, size 24, bits 32, flags 01000001 (little-endian)
[...]
Ctrl: seq 2268, n 4000, size 24, bits 32, flags 01000001 (little-endian)
Ctrl: seq 2269, n 854, size 24, bits 32, flags 01000001 (little-endian)
The log above is 40000 samples streamed at 200kHz into 4000-big
*zio* blocks. In the log above, ``n`` is the number of samples in
each block, ``seq`` is the *zio* sequence number for the block. The
number of bits (32) is wrong, I apologize.
The Output cset
===============
The output channels need some configuration to be provided. This
is done using attributes. Attributes can either be written in
*sysfs* or can be passed in the control block that accompanies data.
This driver defines the sample size as 4 bytes and the trigger should
be configured for a 1-sample block (the library does it at open
time). We should aim at a zero-size data block, but this would require
a patch to *zio*, and I'd better not change version during development.
The output is configured and activated by writing a control block
with proper attributes set. Then a write to the data channel will
push the block to hardware, for it to be activated.
The driver defines the following attributes:::
/* Output ZIO attributes */
enum fd_zattr_out_idx {
FD_ATTR_OUT_MODE = FD_ATTR_DEV__LAST,
FD_ATTR_OUT_REP,
/* Start (or delay) is 4 registers */
FD_ATTR_OUT_START_H,
FD_ATTR_OUT_START_L,
FD_ATTR_OUT_START_COARSE,
FD_ATTR_OUT_START_FINE,
/* End (start + width) is 4 registers */
FD_ATTR_OUT_END_H,
FD_ATTR_OUT_END_L,
FD_ATTR_OUT_END_COARSE,
FD_ATTR_OUT_END_FINE,
/* Delta is 3 registers */
FD_ATTR_OUT_DELTA_L,
FD_ATTR_OUT_DELTA_COARSE,
FD_ATTR_OUT_DELTA_FINE,
/* The two offsets */
FD_ATTR_OUT_DELAY_OFF,
FD_ATTR_OUT_USER_OFF,
FD_ATTR_OUT__LAST,
};
enum fd_output_mode {
FD_OUT_MODE_DISABLED = 0,
FD_OUT_MODE_DELAY,
FD_OUT_MODE_PULSE,
};
To disable the output, you must assign 0 to the mode attribute and
other attributes are ignored. To configure pulse or delay, all
attributes must be set to valid values.
.. note::
writing the output configuration (mode, rep, start, end,
delta) to *sysfs* is not working with this version of *zio*. And I've
been too lazy to add code to do that. While recent developments in *zio*
introduced more complete consistency between the various places where
attributes live, with this version you can only write these attributes to
the control block.
The *delay-offset* attribute represents an offset that is subtracted
from the user-requested delay (*start* fields) when generating output
pulses. It represents internal card delays. The value can be modified
from *sysfs*.
The *user-offset* attribute, which defaults to 0 at module load time, is a
signed value that users can write to represent a number of picoseconds
to be added (or subtracted) to every user command (for both delay
and pulse generation). This is used to account for delays induced by
cabling (range: -2ms to 2ms). The value can be modified
from *sysfs*.
This is the unsorted content of the *sysfs* directory for each
of the output csets:::
spusa# ls -fF /sys/bus/zio/devices/fd-0200/fd-ch1
./ mode end-l user-offset
../ rep end-coarse power/
uevent start-h end-fine trigger/
name start-l delta-l chan0/
enable start-coarse delta-coarse
current_trigger start-fine delta-fine
current_buffer end-h delay-offset
As said, only *delay-offset* and *user-offset* are designed to be
read and written by the user. Additionally, *mode* can be read to
know whether the channel output or delay event has triggered.
As of this version, the other attributes are not
readable nor writable in *sysfs* -- they are meant to be used
in the control block written to */dev*.
Using fd-raw-output
-------------------
The simplest way to generate output is using the tools in ``lib/``.
You are therefore urged to skip this section and read
:ref:`Output Configuration<lib_output>` instead.
For the bravest people, the low
level way to generate output is using *fd-raw-output*, part
of the *tools* directory of this package. The tool writes a control
block to the *zio* control file, setting the block size to 1 32-bit
sample; it then writes 4 bytes to the data file to force output of the
attributes.
The tool acts on channel 1 (the first) by default, but uses the
environment variable ``CHAN`` if set. All arguments on the command
line are passed directly in the attributes. Thus, it is quite a
low-level tool.
To help the user, any number that begins with ``+`` is added to the
current time (in seconds). It is thus recommended to set the card to follow
system time.
The following example sets card time to 0 and programs 10 pulses at
the beginning of the next second. The pulses are 8usec long and
repeat after 16usec. The next example runs 1s of 1kHz square wave.
For readability, numbers are grouped as *(mode, count)*, *(start --
utc-h, utc-l, coarse, frac)*, *(stop -- utc-h, utc-l, coarse, frac)*,
*(delta - utc-l, coarse, frac)*.::
spusa# ./tools/fd-raw-settime 0 0; \
./tools/fd-raw-output 2 10 0 1 0 0 0 1 1000 0 0 2000 0
spusa# ./tools/fd-raw-settime 0 0; \
./tools/fd-raw-output 2 500 0 1 0 0 0 1 62500 0 0 125000 0
The following example sets board time to host time and programs a single
40us pulse at the beginning of the next second (note use of ``+``)::
spusa# echo 0 > /sys/bus/zio/devices/fd-*/command; \
./tools/fd-raw-output 2 0 0 +1 0 0 0 +1 5000 0
The following example programs a pps pulse (1ms long) on channel 1
and a 1MHz square wave on channel 2, assuming board time is already
synchronized with host time:::
spusa# CHAN=1 ./tools/fd-raw-output 2 -1 0 +1 0 0 0 +1 125000 0 1 0 0; \
CHAN=2 ./tools/fd-raw-output 2 -1 0 +1 0 0 0 +1 64 0 0 125 0
.. _dev_cal:
Calibration
===========
Calibration data for a fine-delay card is stored in the I2C FMC EEPROM
device, using the SDB filesystem. Previous versions used a constant
offset of 6kB, but the calibration format was different, so no
compatibility is retained. The driver will refuse to work with cards that have
incompatible EEPROMs, these must be re-calibrated.
The driver automatically loads calibration data from the flash at
initialization time, but only uses it if its hash is valid. The
calibration data is in ``struct fd_calib`` and the on-eeprom structure
is ``fd_calib_on_eeprom``; both are on show in ``fine-delay.h``.
If the hash of the data structure found on EEPROM is not valid, the
driver will use the compile-time default values. You can act on
this configuration using a number of module parameters; please note
that changing calibration data is only expected to happen at production
time.
calibration_check
This integer parameter, if not zero, makes the driver dump the binary
structure of calibration data during initialization.
It is mainly a debug tool.
calibration_default
This option should only be used by developers. If not zero, it tells
the driver to ignore
calibration data found on the EEPROM, thus enacting a build-time
default (which is most likely wrong for any board).
calibration_load
This parameter is a file name, and it should only be used by developers.
The name is used to ask the *firmware loader*
to retrieve a file from ``/lib/firmware``.
The data, once read, is used only
if the size is correct. The hash is regenerated by the driver. Please
remember that all values in the calibration structure are stored as
big-endian.
calibration_save
This option should only be used by developers, and is not supported
in this release. If you are a developer and need to change the calibration,
please check the current master branch on the repository, or a later
release.
The integer parameter is used to request saving calibration data to EEPROM,
whatever values are active after the other parameters have been used.
You can thus save the compiled-in default, the content of the firmware
file just loaded, or the value you just read from EEPROM -- not useful,
but not denied either.
This package currently offers no tool to generate the binary file for
the calibration.
# SPDX-License-Identifier: CC0-1.0
#
# SPDX-FileCopyrightText: 2020 CERN
PROJECT_NAME = "Fmc Fine Delay"
PROJECT_NUMBER = $(GIT_VERSION)
PROJECT_BRIEF = "Fmc Fine Delay"
PROJECT_LOGO =
OUTPUT_DIRECTORY = doxygen-fd-output
CREATE_SUBDIRS = YES
TAB_SIZE = 8
OPTIMIZE_OUTPUT_FOR_C = YES
EXTRACT_STATIC = YES
CASE_SENSE_NAMES = YES
WARN_NO_PARAMDOC = YES
WARN_IF_UNDOCUMENTED = NO
INPUT = $(DOXINPUT)
RECURSIVE = YES
EXCLUDE = $(DOXEXCL)
GENERATE_HTML = NO
GENERATE_LATEX = NO
GENERATE_XML = YES
*.png
\ No newline at end of file
EPSIMAGES := $(shell ls *eps)
all: images
clean:
@rm $(EPSIMAGES:.eps=.png)
images: $(EPSIMAGES:.eps=.png)
%.png: %.eps
@convert $< $@
.PHONY: all clean images
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
Figure 1: Modes of operation of FmcDelay
%!PS-Adobe-3.0 EPSF-3.0
%%BoundingBox: 40 432 545 539
%%LanguageLevel: 1
%%Creator: CorelDRAW 12
%%Title: io_timing.eps
%%CreationDate: Mon Mar 05 10:34:22 2012
%%DocumentProcessColors: Cyan Magenta Yellow Black
%%DocumentSuppliedResources: (atend)
%%EndComments
%%BeginProlog
/AutoFlatness false def
/AutoSteps 0 def
/CMYKMarks true def
/UseLevel 1 def
%Build: CorelDRAW Version 12.154
%Color profile: Generic offset separations profile
/CorelIsEPS true def
%%BeginResource: procset wCorel12Dict 12.0 0
/wCorel12Dict 300 dict def wCorel12Dict begin
% Copyright (c)1992-2003 Corel Corporation
% All rights reserved. v12 r0.0
/bd{bind def}bind def/ld{load def}bd/xd{exch def}bd/_ null def/rp{{pop}repeat}
bd/@cp/closepath ld/@gs/gsave ld/@gr/grestore ld/@np/newpath ld/Tl/translate ld
/$sv 0 def/@sv{/$sv save def}bd/@rs{$sv restore}bd/spg/showpage ld/showpage{}
bd currentscreen/@dsp xd/$dsp/@dsp def/$dsa xd/$dsf xd/$sdf false def/$SDF
false def/$Scra 0 def/SetScr/setscreen ld/@ss{2 index 0 eq{$dsf 3 1 roll 4 -1
roll pop}if exch $Scra add exch load SetScr}bd/SepMode_5 where{pop}{/SepMode_5
0 def}ifelse/CorelIsSeps where{pop}{/CorelIsSeps false def}ifelse
/CorelIsInRIPSeps where{pop}{/CorelIsInRIPSeps false def}ifelse/CorelIsEPS
where{pop}{/CorelIsEPS false def}ifelse/CurrentInkName_5 where{pop}
{/CurrentInkName_5(Composite)def}ifelse/$ink_5 where{pop}{/$ink_5 -1 def}
ifelse/$c 0 def/$m 0 def/$y 0 def/$k 0 def/$t 1 def/$n _ def/$o 0 def/$fil 0
def/$C 0 def/$M 0 def/$Y 0 def/$K 0 def/$T 1 def/$N _ def/$O 0 def/$PF false
def/s1c 0 def/s1m 0 def/s1y 0 def/s1k 0 def/s1t 0 def/s1n _ def/$bkg false def
/SK 0 def/SM 0 def/SY 0 def/SC 0 def/$op false def matrix currentmatrix/$ctm xd
/$ptm matrix def/$ttm matrix def/$stm matrix def/$ffpnt true def
/CorelDrawReencodeVect[16#0/grave 16#5/breve 16#6/dotaccent 16#8/ring
16#A/hungarumlaut 16#B/ogonek 16#C/caron 16#D/dotlessi 16#27/quotesingle
16#60/grave 16#7C/bar 16#80/Euro
16#82/quotesinglbase/florin/quotedblbase/ellipsis/dagger/daggerdbl
16#88/circumflex/perthousand/Scaron/guilsinglleft/OE
16#91/quoteleft/quoteright/quotedblleft/quotedblright/bullet/endash/emdash
16#98/tilde/trademark/scaron/guilsinglright/oe 16#9F/Ydieresis
16#A1/exclamdown/cent/sterling/currency/yen/brokenbar/section
16#a8/dieresis/copyright/ordfeminine/guillemotleft/logicalnot/minus/registered/macron
16#b0/degree/plusminus/twosuperior/threesuperior/acute/mu/paragraph/periodcentered
16#b8/cedilla/onesuperior/ordmasculine/guillemotright/onequarter/onehalf/threequarters/questiondown
16#c0/Agrave/Aacute/Acircumflex/Atilde/Adieresis/Aring/AE/Ccedilla
16#c8/Egrave/Eacute/Ecircumflex/Edieresis/Igrave/Iacute/Icircumflex/Idieresis
16#d0/Eth/Ntilde/Ograve/Oacute/Ocircumflex/Otilde/Odieresis/multiply
16#d8/Oslash/Ugrave/Uacute/Ucircumflex/Udieresis/Yacute/Thorn/germandbls
16#e0/agrave/aacute/acircumflex/atilde/adieresis/aring/ae/ccedilla
16#e8/egrave/eacute/ecircumflex/edieresis/igrave/iacute/icircumflex/idieresis
16#f0/eth/ntilde/ograve/oacute/ocircumflex/otilde/odieresis/divide
16#f8/oslash/ugrave/uacute/ucircumflex/udieresis/yacute/thorn/ydieresis]def
/L2?/languagelevel where{pop languagelevel 2 ge}{false}ifelse def/Comp?{
/LumSepsDict where{pop false}{/AldusSepsDict where{pop false}{1 0 0 0 @gs
setcmykcolor currentcmykcolor @gr add add add 0 ne 0 1 0 0 @gs setcmykcolor
currentcmykcolor @gr add add add 0 ne 0 0 1 0 @gs setcmykcolor currentcmykcolor
@gr add add add 0 ne 0 0 0 1 @gs setcmykcolor currentcmykcolor @gr add add add
0 ne and and and}ifelse}ifelse}bd/@PL{/LV where{pop LV 2 ge L2? not and{@np
/Courier findfont 12 scalefont setfont 72 144 m
(The PostScript level set in the Corel application is higher than)show 72 132 m
(the PostScript level of this device. Change the PS Level in the Corel)show 72
120 m(application to Level 1 by selecting the PostScript tab in the print)show
72 108 m(dialog, and selecting Level 1 from the Compatibility drop down list.)
show flush spg quit}if}if}bd/@BeginSysCorelDict{systemdict/Corel30Dict known
{systemdict/Corel30Dict get exec}if systemdict/CorelLexDict known{1 systemdict
/CorelLexDict get exec}if}bd/@EndSysCorelDict{systemdict/Corel30Dict known
{end}if/EndCorelLexDict where{pop EndCorelLexDict}if}bd AutoFlatness{/@ifl{dup
currentflat exch sub 10 gt{
([Error: PathTooComplex; OffendingCommand: AnyPaintingOperator]\n)print flush
@np exit}{currentflat 2 add setflat}ifelse}bd/@fill/fill ld/fill{currentflat{
{@fill}stopped{@ifl}{exit}ifelse}bind loop setflat}bd/@eofill/eofill ld/eofill
{currentflat{{@eofill}stopped{@ifl}{exit}ifelse}bind loop setflat}bd/@clip
/clip ld/clip{currentflat{{@clip}stopped{@ifl}{exit}ifelse}bind loop setflat}
bd/@eoclip/eoclip ld/eoclip{currentflat{{@eoclip}stopped{@ifl}{exit}ifelse}
bind loop setflat}bd/@stroke/stroke ld/stroke{currentflat{{@stroke}stopped
{@ifl}{exit}ifelse}bind loop setflat}bd}if L2?{/@ssa{true setstrokeadjust}bd}{
/@ssa{}bd}ifelse/d/setdash ld/j/setlinejoin ld/J/setlinecap ld/M/setmiterlimit
ld/w/setlinewidth ld/O{/$o xd}bd/R{/$O xd}bd/W/eoclip ld/c/curveto ld/C/c ld/l
/lineto ld/L/l ld/rl/rlineto ld/m/moveto ld/n/newpath ld/N/newpath ld/P{11 rp}
bd/u{}bd/U{}bd/A{pop}bd/q/@gs ld/Q/@gr ld/&{}bd/@j{@sv @np}bd/@J{@rs}bd/g{1
exch sub/$k xd/$c 0 def/$m 0 def/$y 0 def/$t 1 def/$n _ def/$fil 0 def}bd/G{1
sub neg/$K xd _ 1 0 0 0/$C xd/$M xd/$Y xd/$T xd/$N xd}bd/k{1 index type
/stringtype eq{/$t xd/$n xd}{/$t 0 def/$n _ def}ifelse/$k xd/$y xd/$m xd/$c xd
/$fil 0 def}bd/K{1 index type/stringtype eq{/$T xd/$N xd}{/$T 0 def/$N _ def}
ifelse/$K xd/$Y xd/$M xd/$C xd}bd/x/k ld/X/K ld/sf{1 index type/stringtype eq{
/s1t xd/s1n xd}{/s1t 0 def/s1n _ def}ifelse/s1k xd/s1y xd/s1m xd/s1c xd}bd/i{
dup 0 ne{setflat}{pop}ifelse}bd/v{4 -2 roll 2 copy 6 -2 roll c}bd/V/v ld/y{2
copy c}bd/Y/y ld/@w{matrix rotate/$ptm xd matrix scale $ptm dup concatmatrix
/$ptm xd 1 eq{$ptm exch dup concatmatrix/$ptm xd}if 1 w}bd/@g{1 eq dup/$sdf xd
{/$scp xd/$sca xd/$scf xd}if}bd/@G{1 eq dup/$SDF xd{/$SCP xd/$SCA xd/$SCF xd}
if}bd/@D{2 index 0 eq{$dsf 3 1 roll 4 -1 roll pop}if 3 copy exch $Scra add exch
load SetScr/$dsp xd/$dsa xd/$dsf xd}bd/$ngx{$SDF{$SCF SepMode_5 0 eq{$SCA}
{$dsa}ifelse $SCP @ss}if}bd/@MN{2 copy le{pop}{exch pop}ifelse}bd/@MX{2 copy ge
{pop}{exch pop}ifelse}bd/InRange{3 -1 roll @MN @MX}bd/@sqr{dup 0 rl dup 0 exch
rl neg 0 rl @cp}bd/currentscale{1 0 dtransform matrix defaultmatrix idtransform
dup mul exch dup mul add sqrt 0 1 dtransform matrix defaultmatrix idtransform
dup mul exch dup mul add sqrt}bd/@unscale{}bd/wDstChck{2 1 roll dup 3 -1 roll
eq{1 add}if}bd/@dot{dup mul exch dup mul add 1 exch sub}bd/@lin{exch pop abs 1
exch sub}bd/cmyk2rgb{3{dup 5 -1 roll add 1 exch sub dup 0 lt{pop 0}if exch}
repeat pop}bd/rgb2cmyk{3{1 exch sub 3 1 roll}repeat 3 copy @MN @MN 3{dup 5 -1
roll sub neg exch}repeat}bd/rgb2g{2 index .299 mul 2 index .587 mul add 1 index
.114 mul add 4 1 roll pop pop pop}bd/WaldoColor_5 where{pop}{/SetRgb
/setrgbcolor ld/GetRgb/currentrgbcolor ld/SetGry/setgray ld/GetGry/currentgray
ld/SetRgb2 systemdict/setrgbcolor get def/GetRgb2 systemdict/currentrgbcolor
get def/SetHsb systemdict/sethsbcolor get def/GetHsb systemdict
/currenthsbcolor get def/rgb2hsb{SetRgb2 GetHsb}bd/hsb2rgb{3 -1 roll dup floor
sub 3 1 roll SetHsb GetRgb2}bd/setcmykcolor where{pop/LumSepsDict where{pop
/SetCmyk_5{LumSepsDict/setcmykcolor get exec}def}{/AldusSepsDict where{pop
/SetCmyk_5{AldusSepsDict/setcmykcolor get exec}def}{/SetCmyk_5/setcmykcolor ld
}ifelse}ifelse}{/SetCmyk_5{cmyk2rgb SetRgb}bd}ifelse/currentcmykcolor where{
pop/GetCmyk/currentcmykcolor ld}{/GetCmyk{GetRgb rgb2cmyk}bd}ifelse
/setoverprint where{pop}{/setoverprint{/$op xd}bd}ifelse/currentoverprint where
{pop}{/currentoverprint{$op}bd}ifelse/@tc_5{5 -1 roll dup 1 ge{pop}{4{dup 6 -1
roll mul exch}repeat pop}ifelse}bd/@trp{exch pop 5 1 roll @tc_5}bd
/setprocesscolor_5{SepMode_5 0 eq{SetCmyk_5}{0 4 $ink_5 sub index exch pop 5 1
roll pop pop pop pop SepsColor true eq{$ink_5 3 gt{1 sub neg SetGry}{0 0 0 4
$ink_5 roll SetCmyk_5}ifelse}{1 sub neg SetGry}ifelse}ifelse}bd
/findcmykcustomcolor where{pop}{/findcmykcustomcolor{5 array astore}bd}ifelse
/Corelsetcustomcolor_exists false def/setcustomcolor where{pop
/Corelsetcustomcolor_exists true def}if CorelIsSeps true eq CorelIsInRIPSeps
false eq and{/Corelsetcustomcolor_exists false def}if
Corelsetcustomcolor_exists false eq{/setcustomcolor{exch aload pop SepMode_5 0
eq{pop @tc_5 setprocesscolor_5}{CurrentInkName_5 eq{4 index}{0}ifelse 6 1 roll
5 rp 1 sub neg SetGry}ifelse}bd}if/@scc_5{dup type/booleantype eq{dup
currentoverprint ne{setoverprint}{pop}ifelse}{1 eq setoverprint}ifelse dup _ eq
{pop setprocesscolor_5 pop}{findcmykcustomcolor exch setcustomcolor}ifelse
SepMode_5 0 eq{true}{GetGry 1 eq currentoverprint and not}ifelse}bd/colorimage
where{pop/ColorImage{colorimage}def}{/ColorImage{/ncolors xd/$multi xd $multi
true eq{ncolors 3 eq{/daqB xd/daqG xd/daqR xd pop pop exch pop abs{daqR pop
daqG pop daqB pop}repeat}{/daqK xd/daqY xd/daqM xd/daqC xd pop pop exch pop abs
{daqC pop daqM pop daqY pop daqK pop}repeat}ifelse}{/dataaq xd{dataaq ncolors
dup 3 eq{/$dat xd 0 1 $dat length 3 div 1 sub{dup 3 mul $dat 1 index get 255
div $dat 2 index 1 add get 255 div $dat 3 index 2 add get 255 div rgb2g 255 mul
cvi exch pop $dat 3 1 roll put}for $dat 0 $dat length 3 idiv getinterval pop}{
4 eq{/$dat xd 0 1 $dat length 4 div 1 sub{dup 4 mul $dat 1 index get 255 div
$dat 2 index 1 add get 255 div $dat 3 index 2 add get 255 div $dat 4 index 3
add get 255 div cmyk2rgb rgb2g 255 mul cvi exch pop $dat 3 1 roll put}for $dat
0 $dat length ncolors idiv getinterval}if}ifelse}image}ifelse}bd}ifelse
/setcmykcolor{1 5 1 roll _ currentoverprint @scc_5/$ffpnt xd}bd
/currentcmykcolor{GetCmyk}bd/setrgbcolor{rgb2cmyk setcmykcolor}bd
/currentrgbcolor{currentcmykcolor cmyk2rgb}bd/sethsbcolor{hsb2rgb setrgbcolor}
bd/currenthsbcolor{currentrgbcolor rgb2hsb}bd/setgray{dup dup setrgbcolor}bd
/currentgray{currentrgbcolor rgb2g}bd/InsideDCS false def/IMAGE/image ld/image
{InsideDCS{IMAGE}{/EPSDict where{pop SepMode_5 0 eq{IMAGE}{dup type/dicttype eq
{dup/ImageType get 1 ne{IMAGE}{dup dup/BitsPerComponent get 8 eq exch
/BitsPerComponent get 1 eq or currentcolorspace 0 get/DeviceGray eq and{
CurrentInkName_5(Black)eq{IMAGE}{dup/DataSource get/TCC xd/Height get abs{TCC
pop}repeat}ifelse}{IMAGE}ifelse}ifelse}{2 index 1 ne{CurrentInkName_5(Black)eq
{IMAGE}{/TCC xd pop pop exch pop abs{TCC pop}repeat}ifelse}{IMAGE}ifelse}
ifelse}ifelse}{IMAGE}ifelse}ifelse}bd}ifelse/WaldoColor_5 true def/$fm 0 def
/wfill{1 $fm eq{fill}{eofill}ifelse}bd/@Pf{@sv SepMode_5 0 eq $Psc 0 ne or
$ink_5 3 eq or{0 J 0 j[]0 d $t $c $m $y $k $n $o @scc_5 pop $ctm setmatrix 72
1000 div dup matrix scale dup concat dup Bburx exch Bbury exch itransform
ceiling cvi/Bbury xd ceiling cvi/Bburx xd Bbllx exch Bblly exch itransform
floor cvi/Bblly xd floor cvi/Bbllx xd $Prm aload pop $Psn load exec}{1 SetGry
wfill}ifelse @rs @np}bd/F{matrix currentmatrix $sdf{$scf $sca $scp @ss}if $fil
1 eq{CorelPtrnDoFill}{$fil 2 eq{@ff}{$fil 3 eq{@Pf}{$fil 4 eq
{CorelShfillDoFill}{$t $c $m $y $k $n $o @scc_5{wfill}{@np}ifelse}ifelse}
ifelse}ifelse}ifelse $sdf{$dsf $dsa $dsp @ss}if setmatrix}bd/f{@cp F}bd/S{
matrix currentmatrix $ctm setmatrix $SDF{$SCF $SCA $SCP @ss}if $T $C $M $Y $K
$N $O @scc_5{matrix currentmatrix $ptm concat stroke setmatrix}{@np}ifelse $SDF
{$dsf $dsa $dsp @ss}if setmatrix}bd/s{@cp S}bd/B{@gs F @gr S}bd/b{@cp B}bd/_E{
5 array astore exch cvlit xd}bd/@cc{currentfile $dat readhexstring pop}bd/@sm{
/$ctm $ctm currentmatrix def}bd/@E{/Bbury xd/Bburx xd/Bblly xd/Bbllx xd}bd/@c{
@cp}bd/@P{/$fil 3 def/$Psn xd/$Psc xd array astore/$Prm xd}bd/tcc{@cc}def/@B{
@gs S @gr F}bd/@b{@cp @B}bd/@sep{CurrentInkName_5(Composite)eq{/$ink_5 -1 def}
{CurrentInkName_5(Cyan)eq{/$ink_5 0 def}{CurrentInkName_5(Magenta)eq{/$ink_5 1
def}{CurrentInkName_5(Yellow)eq{/$ink_5 2 def}{CurrentInkName_5(Black)eq
{/$ink_5 3 def}{/$ink_5 4 def}ifelse}ifelse}ifelse}ifelse}ifelse}bd/@whi{@gs
-72000 dup m -72000 72000 l 72000 dup l 72000 -72000 l @cp 1 SetGry fill @gr}
bd/@neg{[{1 exch sub}/exec cvx currenttransfer/exec cvx]cvx settransfer @whi}
bd/deflevel 0 def/@sax{/deflevel deflevel 1 add def}bd/@eax{/deflevel deflevel
dup 0 gt{1 sub}if def deflevel 0 gt{/eax load}{eax}ifelse}bd/eax{{exec}forall}
bd/@rax{deflevel 0 eq{@rs @sv}if}bd systemdict/pdfmark known not{/pdfmark
/cleartomark ld}if/wclip{1 $fm eq{clip}{eoclip}ifelse}bd
% Copyright (c)1992-2003 Corel Corporation
% All rights reserved. v12 r0.0
/z{exch findfont exch scalefont setfont}bd/ZB{9 dict dup begin 4 1 roll
/FontType 3 def/FontMatrix xd/FontBBox xd/Encoding 256 array def 0 1 255{
Encoding exch/.notdef put}for/CharStrings 256 dict def CharStrings/.notdef{}
put/Metrics 256 dict def Metrics/.notdef 3 -1 roll put/BuildChar{exch dup
/$char exch/Encoding get 3 index get def dup/Metrics get $char get aload pop
setcachedevice begin Encoding exch get CharStrings exch get end exec}def end
definefont pop}bd/ZBAddChar{findfont begin dup 4 1 roll dup 6 1 roll Encoding 3
1 roll put CharStrings 3 1 roll put Metrics 3 1 roll put end}bd/Z{findfont dup
maxlength 2 add dict exch dup{1 index/FID ne{3 index 3 1 roll put}{pop pop}
ifelse}forall pop dup dup/Encoding get 256 array copy dup/$fe xd/Encoding exch
put dup/Fontname 3 index put 3 -1 roll dup length 0 ne{0 exch{dup type 0 type
eq{exch pop}{$fe exch 2 index exch put 1 add}ifelse}forall pop}if dup 256 dict
dup/$met xd/Metrics exch put dup/FontMatrix get 0 get 1000 mul 1 exch div 3
index length 256 eq{0 1 255{dup $fe exch get dup/.notdef eq{pop pop}{5 index 3
-1 roll get 2 index mul $met 3 1 roll put}ifelse}for}if pop definefont pop pop
}bd/CorelIsValidCharpath{pathbbox 3 -1 roll sub abs 0.5 ge 3 1 roll sub abs 0.5
ge and}bd/@ftx{{currentpoint 3 -1 roll(0)dup 3 -1 roll 0 exch put dup @gs true
charpath $ctm setmatrix CorelIsValidCharpath{@@txt}if @gr @np stringwidth pop 3
-1 roll add exch m}forall}bd/@ft{matrix currentmatrix exch $sdf{$scf $sca $scp
@ss}if $fil 1 eq{/@@txt/@pf ld @ftx}{$fil 2 eq{/@@txt/@ff ld @ftx}{$fil 3 eq
{/@@txt/@Pf ld @ftx}{$fil 4 eq{/@@txt/CorelShfillDoFill ld @ftx}{$t $c $m $y $k
$n $o @scc_5{show}{pop}ifelse}ifelse}ifelse}ifelse}ifelse $sdf{$dsf $dsa $dsp
@ss}if setmatrix}bd/@st{matrix currentmatrix exch $SDF{$SCF $SCA $SCP @ss}if $T
$C $M $Y $K $N $O @scc_5{{currentpoint 3 -1 roll(0)dup 3 -1 roll 0 exch put dup
@gs true charpath $ctm setmatrix $ptm concat stroke @gr @np stringwidth pop 3
-1 roll add exch m}forall}{pop}ifelse $SDF{$dsf $dsa $dsp @ss}if setmatrix}bd
/@te{@ft}bd/@tr{@st}bd/@ta{dup @gs @ft @gr @st}bd/@t@a{dup @gs @st @gr @ft}bd
/@tm{@sm concat}bd/e{/t{@te}def}bd/r{/t{@tr}def}bd/o{/t{pop}def}bd/a{/t{@ta}
def}bd/@a{/t{@t@a}def}bd/t{@te}def/T{@np $ctm setmatrix/$ttm matrix def}bd/ddt
{t}def/@t{/$stm $stm currentmatrix def 3 1 roll m $ttm concat ddt $stm
setmatrix}bd/@n{/$ttm exch matrix rotate def}bd/@s{}bd/@l{}bd/_lineorientation
0 def/_bitfont null def/_bitlobyte 0 def/_bitkey null def/_bithibyte 0 def
% Copyright (c)1992-2003 Corel Corporation
% All rights reserved. v12 r0.0
/@ii{concat 3 index 3 index m 3 index 1 index l 2 copy l 1 index 3 index l 3
index 3 index l clip pop pop pop pop}bd/@i{@sm @gs @ii 6 index 1 ne{/$frg true
def pop pop}{1 eq{s1t s1c s1m s1y s1k s1n $O @scc_5/$frg xd}{/$frg false def}
ifelse 1 eq{@gs $ctm setmatrix F @gr}if}ifelse @np/$ury xd/$urx xd/$lly xd
/$llx xd/$bts xd/$hei xd/$wid xd/$dat $wid $bts mul 8 div ceiling cvi string
def $bkg $frg or{$SDF{$SCF $SCA $SCP @ss}if $llx $lly Tl $urx $llx sub $ury
$lly sub scale $bkg{$t $c $m $y $k $n $o @scc_5 pop}if $wid $hei abs $bts 1 eq
{$bkg}{$bts}ifelse[$wid 0 0 $hei neg 0 $hei 0 gt{$hei}{0}ifelse]/tcc load $bts
1 eq{imagemask}{image}ifelse $SDF{$dsf $dsa $dsp @ss}if}{$hei abs{tcc pop}
repeat}ifelse @gr $ctm setmatrix}bd/@I{@sm @gs @ii @np/$ury xd/$urx xd/$lly xd
/$llx xd/$ncl xd/$bts xd/$hei xd/$wid xd $ngx $llx $lly Tl $urx $llx sub $ury
$lly sub scale $wid $hei abs $bts[$wid 0 0 $hei neg 0 $hei 0 gt{$hei}{0}ifelse
]$msimage false eq $ncl 1 eq or{/$dat $wid $bts mul $ncl mul 8 div ceiling cvi
string def/@cc load false $ncl ColorImage}{$wid $bts mul 8 div ceiling cvi $ncl
3 eq{dup dup/$dat1 exch string def/$dat2 exch string def/$dat3 exch string def
/@cc1 load/@cc2 load/@cc3 load}{dup dup dup/$dat1 exch string def/$dat2 exch
string def/$dat3 exch string def/$dat4 exch string def/@cc1 load/@cc2 load
/@cc3 load/@cc4 load}ifelse true $ncl ColorImage}ifelse $SDF{$dsf $dsa $dsp
@ss}if @gr $ctm setmatrix}bd/@cc1{currentfile $dat1 readhexstring pop}bd/@cc2{
currentfile $dat2 readhexstring pop}bd/@cc3{currentfile $dat3 readhexstring pop
}bd/@cc4{currentfile $dat4 readhexstring pop}bd/$msimage false def/COMP 0 def
/MaskedImage false def L2?{/@I_2{@sm @gs @ii @np/$ury xd/$urx xd/$lly xd/$llx
xd/$ncl xd/$bts xd/$hei xd/$wid xd/$dat $wid $bts mul $ncl mul 8 div ceiling
cvi string def $ngx $ncl 1 eq{/DeviceGray}{$ncl 3 eq{/DeviceRGB}{/DeviceCMYK}
ifelse}ifelse setcolorspace $llx $lly Tl $urx $llx sub $ury $lly sub scale 8
dict begin/ImageType 1 def/Width $wid def/Height $hei abs def/BitsPerComponent
$bts def/Decode $ncl 1 eq{[0 1]}{$ncl 3 eq{[0 1 0 1 0 1]}{[0 1 0 1 0 1 0 1]}
ifelse}ifelse def/ImageMatrix[$wid 0 0 $hei neg 0 $hei 0 gt{$hei}{0}ifelse]def
/DataSource currentfile/ASCII85Decode filter COMP 1 eq{/DCTDecode filter}{COMP
2 eq{/RunLengthDecode filter}if}ifelse def currentdict end image $SDF{$dsf $dsa
$dsp @ss}if @gr $ctm setmatrix}bd}{/@I_2{}bd}ifelse/@I_3{@sm @gs @ii @np/$ury
xd/$urx xd/$lly xd/$llx xd/$ncl xd/$bts xd/$hei xd/$wid xd/$dat $wid $bts mul
$ncl mul 8 div ceiling cvi string def $ngx $ncl 1 eq{/DeviceGray}{$ncl 3 eq
{/DeviceRGB}{/DeviceCMYK}ifelse}ifelse setcolorspace $llx $lly Tl $urx $llx sub
$ury $lly sub scale/ImageDataDict 8 dict def ImageDataDict begin/ImageType 1
def/Width $wid def/Height $hei abs def/BitsPerComponent $bts def/Decode $ncl 1
eq{[0 1]}{$ncl 3 eq{[0 1 0 1 0 1]}{[0 1 0 1 0 1 0 1]}ifelse}ifelse def
/ImageMatrix[$wid 0 0 $hei neg 0 $hei 0 gt{$hei}{0}ifelse]def/DataSource
currentfile/ASCII85Decode filter COMP 1 eq{/DCTDecode filter}{COMP 2 eq{
/RunLengthDecode filter}if}ifelse def end/MaskedImageDict 7 dict def
MaskedImageDict begin/ImageType 3 def/InterleaveType 3 def/MaskDict
ImageMaskDict def/DataDict ImageDataDict def end MaskedImageDict image $SDF
{$dsf $dsa $dsp @ss}if @gr $ctm setmatrix}bd/@SetMask{/$mbts xd/$mhei xd/$mwid
xd/ImageMaskDict 8 dict def ImageMaskDict begin/ImageType 1 def/Width $mwid def
/Height $mhei abs def/BitsPerComponent $mbts def/DataSource maskstream def
/ImageMatrix[$mwid 0 0 $mhei neg 0 $mhei 0 gt{$mhei}{0}ifelse]def/Decode[1 0]
def end}bd/@daq{dup type/arraytype eq{{}forall}if}bd/@BMP{/@cc xd UseLevel 3 eq
MaskedImage true eq and{7 -2 roll pop pop @I_3}{12 index 1 gt UseLevel 2 eq
UseLevel 3 eq or and{7 -2 roll pop pop @I_2}{11 index 1 eq{12 -1 roll pop @i}{
7 -2 roll pop pop @I}ifelse}ifelse}ifelse}bd
end
%%EndResource
%%EndProlog
%%BeginSetup
wCorel12Dict begin
@BeginSysCorelDict
2.6131 setmiterlimit
1.00 setflat
/$fst 128 def
%%EndSetup
%%Page: 1 1
%LogicalPage: 1
%%BeginPageSetup
@sv
@sm
@sv
%%EndPageSetup
@rax %Note: Object
59.71833 503.18816 59.71946 522.73134 @E
0 J 0 j [] 0 d 0 R 0 @G
0.00 0.00 0.00 1.00 K
0 0.21600 0.21600 0.00000 @w
/$fm 0 def
59.71890 503.18816 m
59.71890 522.73134 L
S
@rax %Note: Object
230.39263 502.14274 230.39376 521.68592 @E
0 J 0 j [] 0 d 0 R 0 @G
0.00 0.00 0.00 1.00 K
0 0.21600 0.21600 0.00000 @w
/$fm 0 def
230.39320 502.14274 m
230.39320 521.68592 L
S
@rax %Note: Object
308.84683 503.18816 308.84797 522.73134 @E
0 J 0 j [] 0 d 0 R 0 @G
0.00 0.00 0.00 1.00 K
0 0.21600 0.21600 0.00000 @w
/$fm 0 def
308.84740 503.18816 m
308.84740 522.73134 L
S
@rax %Note: Object
440.29162 500.70529 440.29276 520.24847 @E
0 J 0 j [] 0 d 0 R 0 @G
0.00 0.00 0.00 1.00 K
0 0.21600 0.21600 0.00000 @w
/$fm 0 def
440.29219 500.70529 m
440.29219 520.24847 L
S
@rax %Note: Object
345.81855 500.28293 345.81969 519.82611 @E
0 J 0 j [] 0 d 0 R 0 @G
0.00 0.00 0.00 1.00 K
0 0.21600 0.21600 0.00000 @w
/$fm 0 def
345.81912 500.28293 m
345.81912 519.82611 L
S
@rax %Note: Object
493.29439 503.18816 493.29553 518.39688 @E
0 J 0 j [] 0 d 0 R 0 @G
0.00 0.00 0.00 1.00 K
0 0.21600 0.21600 0.00000 @w
/$fm 0 def
493.29496 503.18816 m
493.29496 518.39688 L
S
@rax %Note: Object
209.03528 447.81137 357.92362 503.18192 @E
0 J 0 j [] 0 d 0 R 0 @G
0.00 1.00 1.00 0.00 K
0 1.00006 1.00006 0.00000 @w
/$fm 0 def
209.03528 447.81619 m
230.41417 447.81619 L
230.41417 503.18192 L
267.32126 503.18192 L
267.32126 447.84595 L
288.51364 447.82866 L
309.04724 447.81137 L
309.04724 503.17710 L
345.95433 503.17710 L
345.95433 447.84085 L
357.92362 447.84085 L
S
@rax %Note: Object
40.60630 447.81619 98.07874 503.18816 @E
0 J 0 j [] 0 d 0 R 0 @G
1.00 0.00 0.00 0.00 K
0 1.00006 1.00006 0.00000 @w
/$fm 0 def
40.60630 447.84709 m
59.71890 447.84709 L
59.71890 503.18816 L
80.80866 503.18816 L
80.80866 447.81619 L
98.07874 447.81619 L
S
@rax %Note: Object
474.10639 447.81619 531.57883 503.18816 @E
0 J 0 j [1 1 ] 0 d 0 R 0 @G
1.00 0.00 0.00 0.00 K
0 1.00006 1.00006 0.00000 @w
/$fm 0 def
474.10639 447.84709 m
493.21899 447.84709 L
493.21899 503.18816 L
514.30876 503.18816 L
514.30876 447.81619 L
531.57883 447.81619 L
S
@rax %Note: Object
377.01553 447.82328 467.30608 503.18816 @E
0 J 0 j [] 0 d 0 R 0 @G
0.00 1.00 1.00 0.00 K
0 1.00006 1.00006 0.00000 @w
/$fm 0 def
377.01553 447.82328 m
403.37773 447.82328 L
403.37773 503.18901 L
440.28482 503.18901 L
440.28482 447.85276 L
467.30608 447.85276 L
S
@rax %Note: Object
99.98702 447.87458 207.24066 447.87572 @E
0 J 0 j [2 1 ] 0 d 0 R 0 @G
0.00 0.00 0.00 0.20 K
0 1.00006 1.00006 0.00000 @w
/$fm 0 def
99.98702 447.87515 m
207.24066 447.87515 L
S
@rax %Note: Object
358.26435 447.87402 376.85197 447.87515 @E
0 J 0 j [2 1 ] 0 d 0 R 0 @G
0.00 1.00 1.00 0.00 K
0 1.00006 1.00006 0.00000 @w
/$fm 0 def
358.26435 447.87458 m
376.85197 447.87458 L
S
@rax %Note: Object
59.84731 515.47578 230.40482 515.47691 @E
0 J 0 j [] 0 d 0 R 0 @G
0.00 0.00 0.00 1.00 K
0 0.50003 0.50003 0.00000 @w
/$fm 0 def
61.22806 515.47635 m
229.02406 515.47635 L
S
@j
0.00 0.00 0.00 1.00 K
0.00 0.00 0.00 1.00 k
0 @g
0 @G
[] 0 d 0 J 0 j
0 R 0 O
0 1.00800 1.00800 0 @w
64.42356 518.05474 m
60.44598 515.47635 L
64.42356 512.89597 L
S
@J
@j
0.00 0.00 0.00 1.00 K
0.00 0.00 0.00 1.00 k
0 @g
0 @G
[] 0 d 0 J 0 j
0 R 0 O
0 1.00800 1.00800 0 @w
225.82857 518.05474 m
229.80614 515.47635 L
225.82857 512.89597 L
S
@J
@rax %Note: Object
230.39376 515.50441 308.60561 515.50554 @E
0 J 0 j [] 0 d 0 R 0 @G
0.00 0.00 0.00 1.00 K
0 0.50003 0.50003 0.00000 @w
/$fm 0 def
231.77452 515.50498 m
307.22485 515.50498 L
S
@j
0.00 0.00 0.00 1.00 K
0.00 0.00 0.00 1.00 k
0 @g
0 @G
[] 0 d 0 J 0 j
0 R 0 O
0 1.00800 1.00800 0 @w
234.97002 518.08337 m
230.99244 515.50498 L
234.97002 512.92460 L
S
@J
@j
0.00 0.00 0.00 1.00 K
0.00 0.00 0.00 1.00 k
0 @g
0 @G
[] 0 d 0 J 0 j
0 R 0 O
0 1.00800 1.00800 0 @w
304.02935 518.08337 m
308.00693 515.50498 L
304.02935 512.92460 L
S
@J
@rax %Note: Object
309.04214 515.47436 345.48746 515.47550 @E
0 J 0 j [] 0 d 0 R 0 @G
0.00 0.00 0.00 1.00 K
0 0.50003 0.50003 0.00000 @w
/$fm 0 def
310.42290 515.47493 m
344.10671 515.47493 L
S
@j
0.00 0.00 0.00 1.00 K
0.00 0.00 0.00 1.00 k
0 @g
0 @G
[] 0 d 0 J 0 j
0 R 0 O
0 1.00800 1.00800 0 @w
313.61839 518.05332 m
309.64082 515.47493 L
313.61839 512.89455 L
S
@J
@j
0.00 0.00 0.00 1.00 K
0.00 0.00 0.00 1.00 k
0 @g
0 @G
[] 0 d 0 J 0 j
0 R 0 O
0 1.00800 1.00800 0 @w
340.91121 518.05332 m
344.88879 515.47493 L
340.91121 512.89455 L
S
@J
@rax %Note: Object
440.62980 515.67165 493.11581 515.67279 @E
0 J 0 j [] 0 d 0 R 0 @G
0.00 0.00 0.00 1.00 K
0 0.50003 0.50003 0.00000 @w
/$fm 0 def
442.01055 515.67222 m
491.73506 515.67222 L
S
@j
0.00 0.00 0.00 1.00 K
0.00 0.00 0.00 1.00 k
0 @g
0 @G
[] 0 d 0 J 0 j
0 R 0 O
0 1.00800 1.00800 0 @w
445.20605 518.25061 m
441.22847 515.67222 L
445.20605 513.09184 L
S
@J
@j
0.00 0.00 0.00 1.00 K
0.00 0.00 0.00 1.00 k
0 @g
0 @G
[] 0 d 0 J 0 j
0 R 0 O
0 1.00800 1.00800 0 @w
488.53956 518.25061 m
492.51713 515.67222 L
488.53956 513.09184 L
S
@J
@rax %Note: Object
59.84731 481.86113 80.53965 481.86227 @E
0 J 0 j [] 0 d 0 R 0 @G
0.00 0.00 0.00 1.00 K
0 0.50003 0.50003 0.00000 @w
/$fm 0 def
61.22806 481.86170 m
79.15890 481.86170 L
S
@j
0.00 0.00 0.00 1.00 K
0.00 0.00 0.00 1.00 k
0 @g
0 @G
[] 0 d 0 J 0 j
0 R 0 O
0 1.00800 1.00800 0 @w
64.42356 484.44009 m
60.44598 481.86170 L
64.42356 479.28132 L
S
@J
@j
0.00 0.00 0.00 1.00 K
0.00 0.00 0.00 1.00 k
0 @g
0 @G
[] 0 d 0 J 0 j
0 R 0 O
0 1.00800 1.00800 0 @w
75.96340 484.44009 m
79.94098 481.86170 L
75.96340 479.28132 L
S
@J
@rax %Note: Object
267.75269 462.98580 309.16630 462.98693 @E
0 J 0 j [] 0 d 0 R 0 @G
0.00 0.00 0.00 1.00 K
0 0.50003 0.50003 0.00000 @w
/$fm 0 def
269.13345 462.98636 m
307.78554 462.98636 L
S
@j
0.00 0.00 0.00 1.00 K
0.00 0.00 0.00 1.00 k
0 @g
0 @G
[] 0 d 0 J 0 j
0 R 0 O
0 1.00800 1.00800 0 @w
272.32894 465.56476 m
268.35137 462.98636 L
272.32894 460.40598 L
S
@J
@j
0.00 0.00 0.00 1.00 K
0.00 0.00 0.00 1.00 k
0 @g
0 @G
[] 0 d 0 J 0 j
0 R 0 O
0 1.00800 1.00800 0 @w
304.59005 465.56476 m
308.56762 462.98636 L
304.59005 460.40598 L
S
@J
@rax 41.59389 432.90822 94.32879 442.17156 @E
[0.00028346 0.00000000 0.00000000 0.00028346 41.59388824 435.01321243] @tm
0 O 0 @g
1.00 0.00 0.00 0.00 k
e
% FontChange:/_ArialMT 35278.00000 z
%CHAR: 0 0 (t) @t
/$fm 1 def
9096 2775 m
9543 35 L
8673 -147 7891 -241 7203 -241 c
6074 -241 5204 -65 4580 294 c
3963 647 3528 1117 3275 1699 c
3022 2275 2893 3498 2893 5356 c
2893 15881 L
617 15881 L
617 18292 L
2893 18292 L
2893 22825 L
5980 24683 L
5980 18292 L
9096 18292 L
9096 15881 L
5980 15881 L
5980 5186 l
5980 4298 6033 3734 6138 3481 c
6250 3228 6426 3028 6673 2875 c
6920 2728 7273 2652 7732 2652 c
8079 2652 8531 2693 9096 2775 C
@c
F
%CHAR: 9801 0 (r) @t
/$fm 1 def
12094 0 m
12094 18292 L
14881 18292 L
14881 15522 L
15592 16816 16251 17674 16857 18086 c
17456 18497 18121 18709 18844 18709 c
19891 18709 20949 18374 22031 17710 C
20961 14829 L
20208 15281 19450 15505 18691 15505 c
18009 15505 17403 15299 16862 14893 c
16322 14482 15939 13917 15710 13194 c
15363 12094 15193 10889 15193 9578 c
15193 0 L
12094 0 L
@c
F
%CHAR: 21549 0 (i) @t
/$fm 1 def
23889 21684 m
23889 25253 L
26994 25253 L
26994 21684 L
23889 21684 L
@c
23889 0 m
23889 18292 L
26994 18292 L
26994 0 L
23889 0 L
@c
F
%CHAR: 29387 0 (g) @t
/$fm 1 def
31145 -1517 m
34161 -1964 L
34285 -2893 34638 -3569 35208 -3998 c
35978 -4569 37031 -4857 38359 -4857 c
39800 -4857 40905 -4569 41687 -3998 c
42469 -3422 42993 -2616 43269 -1588 c
43434 -953 43504 370 43492 2393 C
42140 800 40453 0 38430 0 c
35913 0 33967 905 32591 2722 c
31216 4533 30522 6715 30522 9249 c
30522 10995 30839 12606 31468 14082 c
32103 15558 33021 16698 34220 17504 c
35420 18303 36831 18709 38448 18709 c
40605 18709 42387 17833 43786 16087 C
43786 18292 L
46650 18292 L
46650 2481 l
46650 -365 46356 -2387 45780 -3575 c
45197 -4763 44280 -5703 43022 -6391 c
41764 -7079 40217 -7426 38377 -7426 c
36196 -7426 34432 -6932 33091 -5950 c
31745 -4968 31098 -3493 31145 -1517 C
@c
33709 9472 m
33709 7073 34185 5321 35143 4222 c
36096 3116 37289 2569 38724 2569 c
40147 2569 41340 3116 42305 4210 c
43269 5309 43751 7026 43751 9372 c
43751 11612 43257 13300 42263 14435 c
41270 15569 40070 16140 38671 16140 c
37295 16140 36125 15581 35155 14458 c
34191 13341 33709 11677 33709 9472 c
@c
F
%CHAR: 49007 0 (g) @t
/$fm 1 def
50765 -1517 m
53781 -1964 L
53905 -2893 54258 -3569 54828 -3998 c
55598 -4569 56651 -4857 57979 -4857 c
59420 -4857 60525 -4569 61307 -3998 c
62089 -3422 62613 -2616 62889 -1588 c
63054 -953 63124 370 63112 2393 C
61760 800 60073 0 58050 0 c
55533 0 53587 905 52211 2722 c
50836 4533 50142 6715 50142 9249 c
50142 10995 50459 12606 51088 14082 c
51723 15558 52641 16698 53840 17504 c
55040 18303 56451 18709 58068 18709 c
60225 18709 62007 17833 63406 16087 C
63406 18292 L
66270 18292 L
66270 2481 l
66270 -365 65976 -2387 65400 -3575 c
64817 -4763 63900 -5703 62642 -6391 c
61384 -7079 59837 -7426 57997 -7426 c
55816 -7426 54052 -6932 52711 -5950 c
51365 -4968 50718 -3493 50765 -1517 C
@c
53329 9472 m
53329 7073 53805 5321 54763 4222 c
55716 3116 56909 2569 58344 2569 c
59767 2569 60960 3116 61925 4210 c
62889 5309 63371 7026 63371 9372 c
63371 11612 62877 13300 61883 14435 c
60890 15569 59690 16140 58291 16140 c
56915 16140 55745 15581 54775 14458 c
53811 13341 53329 11677 53329 9472 c
@c
F
%CHAR: 68627 0 (e) @t
/$fm 1 def
83473 5891 m
86678 5497 L
86172 3622 85237 2170 83873 1135 c
82503 106 80757 -412 78634 -412 c
75959 -412 73836 412 72272 2058 c
70703 3704 69921 6015 69921 8990 c
69921 12071 70714 14458 72296 16157 c
73883 17857 75935 18709 78464 18709 c
80910 18709 82909 17874 84455 16210 c
86007 14546 86783 12200 86783 9184 c
86783 8996 86778 8720 86766 8355 C
73125 8355 L
73237 6344 73807 4804 74830 3739 c
75853 2669 77123 2134 78652 2134 c
79787 2134 80757 2434 81562 3034 c
82368 3628 83003 4580 83473 5891 C
@c
73295 10907 m
83508 10907 L
83373 12441 82979 13600 82338 14364 c
81351 15558 80069 16157 78499 16157 c
77076 16157 75877 15681 74906 14729 c
73936 13776 73401 12500 73295 10907 C
@c
F
%CHAR: 88247 0 (r) @t
/$fm 1 def
90540 0 m
90540 18292 L
93327 18292 L
93327 15522 L
94038 16816 94697 17674 95303 18086 c
95902 18497 96567 18709 97290 18709 c
98337 18709 99395 18374 100477 17710 C
99407 14829 L
98654 15281 97896 15505 97137 15505 c
96455 15505 95849 15299 95308 14893 c
94768 14482 94385 13917 94156 13194 c
93809 12094 93639 10889 93639 9578 c
93639 0 L
90540 0 L
@c
F
%CHAR: 109796 0 (i) @t
/$fm 1 def
112136 21684 m
112136 25253 L
115241 25253 L
115241 21684 L
112136 21684 L
@c
112136 0 m
112136 18292 L
115241 18292 L
115241 0 L
112136 0 L
@c
F
%CHAR: 117634 0 (n) @t
/$fm 1 def
119962 0 m
119962 18292 L
122749 18292 L
122749 15693 L
124096 17704 126036 18709 128570 18709 c
129676 18709 130687 18509 131610 18115 c
132539 17715 133227 17198 133685 16551 c
134150 15910 134467 15146 134656 14264 c
134767 13688 134826 12682 134826 11248 c
134826 0 L
131722 0 L
131722 11130 l
131722 12388 131604 13335 131363 13964 c
131122 14587 130693 15087 130081 15458 c
129464 15834 128747 16022 127918 16022 c
126595 16022 125460 15599 124496 14764 c
123537 13923 123061 12336 123061 9990 c
123061 0 L
119962 0 L
@c
F
%CHAR: 137254 0 (p) @t
/$fm 1 def
139582 -7009 m
139582 18292 L
142405 18292 L
142405 15916 L
143069 16845 143822 17545 144662 18009 c
145497 18474 146514 18709 147708 18709 c
149272 18709 150648 18303 151841 17504 c
153041 16698 153940 15563 154546 14099 c
155158 12635 155463 11030 155463 9284 c
155463 7414 155128 5727 154452 4227 c
153782 2728 152806 1582 151524 782 c
150242 -12 148896 -412 147485 -412 c
146450 -412 145527 -194 144704 241 c
143880 676 143210 1229 142681 1893 C
142681 -7009 L
139582 -7009 L
@c
142387 9043 m
142387 6691 142863 4951 143816 3822 c
144768 2699 145927 2134 147279 2134 c
148655 2134 149836 2716 150818 3886 c
151800 5051 152294 6856 152294 9302 c
152294 11636 151812 13376 150854 14540 c
149895 15699 148749 16281 147420 16281 c
146097 16281 144927 15663 143910 14429 c
142893 13194 142387 11395 142387 9043 c
@c
F
%CHAR: 156874 0 (u) @t
/$fm 1 def
171191 0 m
171191 2687 L
169762 617 167828 -412 165382 -412 c
164306 -412 163295 -206 162360 206 c
161425 617 160731 1141 160278 1764 c
159820 2393 159502 3157 159320 4063 c
159196 4674 159132 5639 159132 6962 c
159132 18292 L
162230 18292 L
162230 8149 l
162230 6526 162295 5439 162419 4874 c
162618 4057 163030 3422 163659 2952 c
164294 2487 165076 2258 166005 2258 c
166934 2258 167804 2493 168622 2969 c
169439 3445 170015 4098 170350 4915 c
170691 5739 170862 6932 170862 8490 c
170862 18292 L
173960 18292 L
173960 0 L
171191 0 L
@c
F
%CHAR: 176494 0 (t) @t
/$fm 1 def
185590 2775 m
186037 35 L
185167 -147 184385 -241 183697 -241 c
182568 -241 181698 -65 181074 294 c
180457 647 180022 1117 179769 1699 c
179516 2275 179387 3498 179387 5356 c
179387 15881 L
177111 15881 L
177111 18292 L
179387 18292 L
179387 22825 L
182474 24683 L
182474 18292 L
185590 18292 L
185590 15881 L
182474 15881 L
182474 5186 l
182474 4298 182527 3734 182632 3481 c
182744 3228 182920 3028 183167 2875 c
183414 2728 183767 2652 184226 2652 c
184573 2652 185025 2693 185590 2775 C
@c
F
T
@rax 467.89398 432.90822 544.18847 442.17156 @E
[0.00028346 0.00000000 0.00000000 0.00028346 467.89396714 435.01321243] @tm
0 O 0 @g
1.00 0.00 0.00 0.00 k
e
% FontChange:/_ArialMT 35278.00000 z
%CHAR: 0 0 (n) @t
/$fm 1 def
2328 0 m
2328 18292 L
5115 18292 L
5115 15693 L
6462 17704 8402 18709 10936 18709 c
12042 18709 13053 18509 13976 18115 c
14905 17715 15593 17198 16051 16551 c
16516 15910 16833 15146 17022 14264 c
17133 13688 17192 12682 17192 11248 c
17192 0 L
14088 0 L
14088 11130 l
14088 12388 13970 13335 13729 13964 c
13488 14587 13059 15087 12447 15458 c
11830 15834 11113 16022 10284 16022 c
8961 16022 7826 15599 6862 14764 c
5903 13923 5427 12336 5427 9990 c
5427 0 L
2328 0 L
@c
F
%CHAR: 19620 0 (e) @t
/$fm 1 def
34466 5891 m
37671 5497 L
37165 3622 36230 2170 34866 1135 c
33496 106 31750 -412 29627 -412 c
26952 -412 24829 412 23265 2058 c
21696 3704 20914 6015 20914 8990 c
20914 12071 21707 14458 23289 16157 c
24876 17857 26928 18709 29457 18709 c
31903 18709 33902 17874 35448 16210 c
37000 14546 37776 12200 37776 9184 c
37776 8996 37771 8720 37759 8355 C
24118 8355 L
24230 6344 24800 4804 25823 3739 c
26846 2669 28116 2134 29645 2134 c
30780 2134 31750 2434 32555 3034 c
33361 3628 33996 4580 34466 5891 C
@c
24288 10907 m
34501 10907 L
34366 12441 33972 13600 33331 14364 c
32344 15558 31062 16157 29492 16157 c
28069 16157 26870 15681 25899 14729 c
24929 13776 24394 12500 24288 10907 C
@c
F
%CHAR: 39240 0 (x) @t
/$fm 1 def
39499 0 m
46184 9507 L
39998 18292 L
43873 18292 L
46684 14005 l
47207 13188 47636 12506 47954 11953 C
48459 12712 48924 13382 49353 13970 c
52434 18292 L
56138 18292 L
49818 9678 L
56620 0 L
52816 0 L
49059 5686 L
48060 7220 L
43256 0 L
39499 0 L
@c
F
%CHAR: 56879 0 (t) @t
/$fm 1 def
65975 2775 m
66422 35 L
65552 -147 64770 -241 64082 -241 c
62953 -241 62083 -65 61459 294 c
60842 647 60407 1117 60154 1699 c
59901 2275 59772 3498 59772 5356 c
59772 15881 L
57496 15881 L
57496 18292 L
59772 18292 L
59772 22825 L
62859 24683 L
62859 18292 L
65975 18292 L
65975 15881 L
62859 15881 L
62859 5186 l
62859 4298 62912 3734 63017 3481 c
63129 3228 63305 3028 63552 2875 c
63799 2728 64152 2652 64611 2652 c
64958 2652 65410 2693 65975 2775 C
@c
F
%CHAR: 76481 0 (t) @t
/$fm 1 def
85577 2775 m
86024 35 L
85154 -147 84372 -241 83684 -241 c
82555 -241 81685 -65 81061 294 c
80444 647 80009 1117 79756 1699 c
79503 2275 79374 3498 79374 5356 c
79374 15881 L
77098 15881 L
77098 18292 L
79374 18292 L
79374 22825 L
82461 24683 L
82461 18292 L
85577 18292 L
85577 15881 L
82461 15881 L
82461 5186 l
82461 4298 82514 3734 82619 3481 c
82731 3228 82907 3028 83154 2875 c
83401 2728 83754 2652 84213 2652 c
84560 2652 85012 2693 85577 2775 C
@c
F
%CHAR: 86282 0 (r) @t
/$fm 1 def
88575 0 m
88575 18292 L
91362 18292 L
91362 15522 L
92073 16816 92732 17674 93338 18086 c
93937 18497 94602 18709 95325 18709 c
96372 18709 97430 18374 98512 17710 C
97442 14829 L
96689 15281 95931 15505 95172 15505 c
94490 15505 93884 15299 93343 14893 c
92803 14482 92420 13917 92191 13194 c
91844 12094 91674 10889 91674 9578 c
91674 0 L
88575 0 L
@c
F
%CHAR: 98030 0 (i) @t
/$fm 1 def
100370 21684 m
100370 25253 L
103475 25253 L
103475 21684 L
100370 21684 L
@c
100370 0 m
100370 18292 L
103475 18292 L
103475 0 L
100370 0 L
@c
F
%CHAR: 105868 0 (g) @t
/$fm 1 def
107626 -1517 m
110642 -1964 L
110766 -2893 111119 -3569 111689 -3998 c
112459 -4569 113512 -4857 114840 -4857 c
116281 -4857 117386 -4569 118168 -3998 c
118950 -3422 119474 -2616 119750 -1588 c
119915 -953 119985 370 119973 2393 C
118621 800 116934 0 114911 0 c
112394 0 110448 905 109072 2722 c
107697 4533 107003 6715 107003 9249 c
107003 10995 107320 12606 107949 14082 c
108584 15558 109502 16698 110701 17504 c
111901 18303 113312 18709 114929 18709 c
117086 18709 118868 17833 120267 16087 C
120267 18292 L
123131 18292 L
123131 2481 l
123131 -365 122837 -2387 122261 -3575 c
121678 -4763 120761 -5703 119503 -6391 c
118245 -7079 116698 -7426 114858 -7426 c
112677 -7426 110913 -6932 109572 -5950 c
108226 -4968 107579 -3493 107626 -1517 C
@c
110190 9472 m
110190 7073 110666 5321 111624 4222 c
112577 3116 113770 2569 115205 2569 c
116628 2569 117821 3116 118786 4210 c
119750 5309 120232 7026 120232 9372 c
120232 11612 119738 13300 118744 14435 c
117751 15569 116551 16140 115152 16140 c
113776 16140 112606 15581 111636 14458 c
110672 13341 110190 11677 110190 9472 c
@c
F
%CHAR: 125488 0 (g) @t
/$fm 1 def
127246 -1517 m
130262 -1964 L
130386 -2893 130739 -3569 131309 -3998 c
132079 -4569 133132 -4857 134460 -4857 c
135901 -4857 137006 -4569 137788 -3998 c
138570 -3422 139094 -2616 139370 -1588 c
139535 -953 139605 370 139593 2393 C
138241 800 136554 0 134531 0 c
132014 0 130068 905 128692 2722 c
127317 4533 126623 6715 126623 9249 c
126623 10995 126940 12606 127569 14082 c
128204 15558 129122 16698 130321 17504 c
131521 18303 132932 18709 134549 18709 c
136706 18709 138488 17833 139887 16087 C
139887 18292 L
142751 18292 L
142751 2481 l
142751 -365 142457 -2387 141881 -3575 c
141298 -4763 140381 -5703 139123 -6391 c
137865 -7079 136318 -7426 134478 -7426 c
132297 -7426 130533 -6932 129192 -5950 c
127846 -4968 127199 -3493 127246 -1517 C
@c
129810 9472 m
129810 7073 130286 5321 131244 4222 c
132197 3116 133390 2569 134825 2569 c
136248 2569 137441 3116 138406 4210 c
139370 5309 139852 7026 139852 9372 c
139852 11612 139358 13300 138364 14435 c
137371 15569 136171 16140 134772 16140 c
133396 16140 132226 15581 131256 14458 c
130292 13341 129810 11677 129810 9472 c
@c
F
%CHAR: 145108 0 (e) @t
/$fm 1 def
159954 5891 m
163159 5497 L
162653 3622 161718 2170 160354 1135 c
158984 106 157238 -412 155115 -412 c
152440 -412 150317 412 148753 2058 c
147184 3704 146402 6015 146402 8990 c
146402 12071 147195 14458 148777 16157 c
150364 17857 152416 18709 154945 18709 c
157391 18709 159390 17874 160936 16210 c
162488 14546 163264 12200 163264 9184 c
163264 8996 163259 8720 163247 8355 C
149606 8355 L
149718 6344 150288 4804 151311 3739 c
152334 2669 153604 2134 155133 2134 c
156268 2134 157238 2434 158043 3034 c
158849 3628 159484 4580 159954 5891 C
@c
149776 10907 m
159989 10907 L
159854 12441 159460 13600 158819 14364 c
157832 15558 156550 16157 154980 16157 c
153557 16157 152358 15681 151387 14729 c
150417 13776 149882 12500 149776 10907 C
@c
F
%CHAR: 164728 0 (r) @t
/$fm 1 def
167021 0 m
167021 18292 L
169808 18292 L
169808 15522 L
170519 16816 171178 17674 171784 18086 c
172383 18497 173048 18709 173771 18709 c
174818 18709 175876 18374 176958 17710 C
175888 14829 L
175135 15281 174377 15505 173618 15505 c
172936 15505 172330 15299 171789 14893 c
171249 14482 170866 13917 170637 13194 c
170290 12094 170120 10889 170120 9578 c
170120 0 L
167021 0 L
@c
F
%CHAR: 186277 0 (p) @t
/$fm 1 def
188605 -7009 m
188605 18292 L
191428 18292 L
191428 15916 L
192092 16845 192845 17545 193685 18009 c
194520 18474 195537 18709 196731 18709 c
198295 18709 199671 18303 200864 17504 c
202064 16698 202963 15563 203569 14099 c
204181 12635 204486 11030 204486 9284 c
204486 7414 204151 5727 203475 4227 c
202805 2728 201829 1582 200547 782 c
199265 -12 197919 -412 196508 -412 c
195473 -412 194550 -194 193727 241 c
192903 676 192233 1229 191704 1893 C
191704 -7009 L
188605 -7009 L
@c
191410 9043 m
191410 6691 191886 4951 192839 3822 c
193791 2699 194950 2134 196302 2134 c
197678 2134 198859 2716 199841 3886 c
200823 5051 201317 6856 201317 9302 c
201317 11636 200835 13376 199877 14540 c
198918 15699 197772 16281 196443 16281 c
195120 16281 193950 15663 192933 14429 c
191916 13194 191410 11395 191410 9043 c
@c
F
%CHAR: 205897 0 (u) @t
/$fm 1 def
220214 0 m
220214 2687 L
218785 617 216851 -412 214405 -412 c
213329 -412 212318 -206 211383 206 c
210448 617 209754 1141 209301 1764 c
208843 2393 208525 3157 208343 4063 c
208219 4674 208155 5639 208155 6962 c
208155 18292 L
211253 18292 L
211253 8149 l
211253 6526 211318 5439 211442 4874 c
211641 4057 212053 3422 212682 2952 c
213317 2487 214099 2258 215028 2258 c
215957 2258 216827 2493 217645 2969 c
218462 3445 219038 4098 219373 4915 c
219714 5739 219885 6932 219885 8490 c
219885 18292 L
222983 18292 L
222983 0 L
220214 0 L
@c
F
%CHAR: 225517 0 (l) @t
/$fm 1 def
227775 0 m
227775 25253 L
230873 25253 L
230873 0 L
227775 0 L
@c
F
%CHAR: 233355 0 (s) @t
/$fm 1 def
234443 5462 m
237506 5944 L
237677 4715 238159 3775 238947 3116 c
239729 2464 240828 2134 242245 2134 c
243668 2134 244726 2428 245414 3005 c
246102 3587 246449 4269 246449 5045 c
246449 5750 246143 6297 245532 6703 c
245108 6979 244050 7326 242363 7749 c
240093 8326 238511 8820 237635 9243 c
236759 9660 236089 10242 235636 10983 c
235184 11724 234954 12541 234954 13435 c
234954 14252 235142 15005 235519 15699 c
235889 16398 236401 16975 237042 17433 c
237524 17786 238182 18092 239011 18339 c
239846 18586 240740 18709 241692 18709 c
243127 18709 244385 18497 245473 18086 c
246561 17674 247360 17116 247878 16410 c
248395 15699 248748 14758 248942 13576 C
245914 13159 L
245773 14099 245373 14834 244715 15364 c
244056 15893 243121 16157 241916 16157 c
240493 16157 239476 15922 238870 15452 c
238259 14981 237953 14429 237953 13800 c
237953 13394 238082 13035 238335 12712 c
238588 12377 238982 12106 239523 11883 c
239834 11771 240746 11507 242263 11095 c
244456 10507 245985 10031 246849 9654 c
247719 9284 248401 8737 248895 8026 c
249389 7314 249636 6432 249636 5374 c
249636 4339 249330 3369 248730 2452 c
248125 1540 247255 835 246120 335 c
244985 -165 243697 -412 242263 -412 c
239881 -412 238070 82 236824 1070 c
235578 2058 234784 3522 234443 5462 C
@c
F
%CHAR: 250994 0 (e) @t
/$fm 1 def
265840 5891 m
269045 5497 L
268539 3622 267604 2170 266240 1135 c
264870 106 263124 -412 261001 -412 c
258326 -412 256203 412 254639 2058 c
253070 3704 252288 6015 252288 8990 c
252288 12071 253081 14458 254663 16157 c
256250 17857 258302 18709 260831 18709 c
263277 18709 265276 17874 266822 16210 c
268374 14546 269150 12200 269150 9184 c
269150 8996 269145 8720 269133 8355 C
255492 8355 L
255604 6344 256174 4804 257197 3739 c
258220 2669 259490 2134 261019 2134 c
262154 2134 263124 2434 263929 3034 c
264735 3628 265370 4580 265840 5891 C
@c
255662 10907 m
265875 10907 L
265740 12441 265346 13600 264705 14364 c
263718 15558 262436 16157 260866 16157 c
259443 16157 258244 15681 257273 14729 c
256303 13776 255768 12500 255662 10907 C
@c
F
T
@rax 320.05928 432.82035 359.16066 442.20529 @E
[0.00028346 0.00000000 0.00000000 0.00028346 320.05926388 434.92533842] @tm
0 O 0 @g
0.00 1.00 1.00 0.00 k
e
% FontChange:/_ArialMT 35278.00000 z
%CHAR: 0 0 (o) @t
/$fm 1 def
1170 9149 m
1170 12535 2111 15046 3998 16675 c
5568 18027 7485 18709 9748 18709 c
12265 18709 14323 17886 15916 16234 c
17516 14587 18309 12312 18309 9407 c
18309 7050 17957 5198 17251 3851 c
16545 2499 15516 1452 14170 706 c
12818 -41 11348 -412 9748 -412 c
7191 -412 5121 406 3540 2052 c
1958 3692 1170 6056 1170 9149 c
@c
4357 9149 m
4357 6803 4868 5051 5891 3886 c
6914 2716 8202 2134 9748 2134 c
11289 2134 12571 2722 13594 3892 c
14611 5062 15123 6850 15123 9249 c
15123 11512 14611 13229 13582 14393 c
12553 15558 11277 16140 9748 16140 c
8202 16140 6914 15558 5891 14399 c
4868 13241 4357 11489 4357 9149 c
@c
F
%CHAR: 19620 0 (u) @t
/$fm 1 def
33937 0 m
33937 2687 L
32508 617 30574 -412 28128 -412 c
27052 -412 26041 -206 25106 206 c
24171 617 23477 1141 23024 1764 c
22566 2393 22248 3157 22066 4063 c
21942 4674 21878 5639 21878 6962 c
21878 18292 L
24976 18292 L
24976 8149 l
24976 6526 25041 5439 25165 4874 c
25364 4057 25776 3422 26405 2952 c
27040 2487 27822 2258 28751 2258 c
29680 2258 30550 2493 31368 2969 c
32185 3445 32761 4098 33096 4915 c
33437 5739 33608 6932 33608 8490 c
33608 18292 L
36706 18292 L
36706 0 L
33937 0 L
@c
F
%CHAR: 39240 0 (t) @t
/$fm 1 def
48336 2775 m
48783 35 L
47913 -147 47131 -241 46443 -241 c
45314 -241 44444 -65 43820 294 c
43203 647 42768 1117 42515 1699 c
42262 2275 42133 3498 42133 5356 c
42133 15881 L
39857 15881 L
39857 18292 L
42133 18292 L
42133 22825 L
45220 24683 L
45220 18292 L
48336 18292 L
48336 15881 L
45220 15881 L
45220 5186 l
45220 4298 45273 3734 45378 3481 c
45490 3228 45666 3028 45913 2875 c
46160 2728 46513 2652 46972 2652 c
47319 2652 47771 2693 48336 2775 C
@c
F
%CHAR: 49041 0 (p) @t
/$fm 1 def
51369 -7009 m
51369 18292 L
54192 18292 L
54192 15916 L
54856 16845 55609 17545 56449 18009 c
57284 18474 58301 18709 59495 18709 c
61059 18709 62435 18303 63628 17504 c
64828 16698 65727 15563 66333 14099 c
66945 12635 67250 11030 67250 9284 c
67250 7414 66915 5727 66239 4227 c
65569 2728 64593 1582 63311 782 c
62029 -12 60683 -412 59272 -412 c
58237 -412 57314 -194 56491 241 c
55667 676 54997 1229 54468 1893 C
54468 -7009 L
51369 -7009 L
@c
54174 9043 m
54174 6691 54650 4951 55603 3822 c
56555 2699 57714 2134 59066 2134 c
60442 2134 61623 2716 62605 3886 c
63587 5051 64081 6856 64081 9302 c
64081 11636 63599 13376 62641 14540 c
61682 15699 60536 16281 59207 16281 c
57884 16281 56714 15663 55697 14429 c
54680 13194 54174 11395 54174 9043 c
@c
F
%CHAR: 68661 0 (u) @t
/$fm 1 def
82978 0 m
82978 2687 L
81549 617 79615 -412 77169 -412 c
76093 -412 75082 -206 74147 206 c
73212 617 72518 1141 72065 1764 c
71607 2393 71289 3157 71107 4063 c
70983 4674 70919 5639 70919 6962 c
70919 18292 L
74017 18292 L
74017 8149 l
74017 6526 74082 5439 74206 4874 c
74405 4057 74817 3422 75446 2952 c
76081 2487 76863 2258 77792 2258 c
78721 2258 79591 2493 80409 2969 c
81226 3445 81802 4098 82137 4915 c
82478 5739 82649 6932 82649 8490 c
82649 18292 L
85747 18292 L
85747 0 L
82978 0 L
@c
F
%CHAR: 88281 0 (t) @t
/$fm 1 def
97377 2775 m
97824 35 L
96954 -147 96172 -241 95484 -241 c
94355 -241 93485 -65 92861 294 c
92244 647 91809 1117 91556 1699 c
91303 2275 91174 3498 91174 5356 c
91174 15881 L
88898 15881 L
88898 18292 L
91174 18292 L
91174 22825 L
94261 24683 L
94261 18292 L
97377 18292 L
97377 15881 L
94261 15881 L
94261 5186 l
94261 4298 94314 3734 94419 3481 c
94531 3228 94707 3028 94954 2875 c
95201 2728 95554 2652 96013 2652 c
96360 2652 96812 2693 97377 2775 C
@c
F
%CHAR: 98082 0 (\050) @t
/$fm 1 def
106331 -7426 m
104620 -5268 103174 -2740 101992 153 c
100810 3052 100216 6044 100216 9149 c
100216 11883 100657 14499 101545 17004 c
102580 19909 104173 22801 106331 25682 C
108554 25682 L
107166 23295 106249 21590 105802 20567 c
105096 18985 104550 17327 104144 15605 c
103650 13459 103403 11301 103403 9131 c
103403 3604 105120 -1911 108554 -7426 C
106331 -7426 L
@c
F
%CHAR: 109830 0 (s) @t
/$fm 1 def
110918 5462 m
113981 5944 L
114152 4715 114634 3775 115422 3116 c
116204 2464 117303 2134 118720 2134 c
120143 2134 121201 2428 121889 3005 c
122577 3587 122924 4269 122924 5045 c
122924 5750 122618 6297 122007 6703 c
121583 6979 120525 7326 118838 7749 c
116568 8326 114986 8820 114110 9243 c
113234 9660 112564 10242 112111 10983 c
111659 11724 111429 12541 111429 13435 c
111429 14252 111617 15005 111994 15699 c
112364 16398 112876 16975 113517 17433 c
113999 17786 114657 18092 115486 18339 c
116321 18586 117215 18709 118167 18709 c
119602 18709 120860 18497 121948 18086 c
123036 17674 123835 17116 124353 16410 c
124870 15699 125223 14758 125417 13576 C
122389 13159 L
122248 14099 121848 14834 121190 15364 c
120531 15893 119596 16157 118391 16157 c
116968 16157 115951 15922 115345 15452 c
114734 14981 114428 14429 114428 13800 c
114428 13394 114557 13035 114810 12712 c
115063 12377 115457 12106 115998 11883 c
116309 11771 117221 11507 118738 11095 c
120931 10507 122460 10031 123324 9654 c
124194 9284 124876 8737 125370 8026 c
125864 7314 126111 6432 126111 5374 c
126111 4339 125805 3369 125205 2452 c
124600 1540 123730 835 122595 335 c
121460 -165 120172 -412 118738 -412 c
116356 -412 114545 82 113299 1070 c
112053 2058 111259 3522 110918 5462 C
@c
F
%CHAR: 127469 0 (\051) @t
/$fm 1 def
131826 -7426 m
129603 -7426 L
133037 -1911 134754 3604 134754 9131 c
134754 11289 134507 13429 134013 15558 c
133625 17274 133078 18933 132379 20514 c
131932 21549 131009 23272 129603 25682 C
131826 25682 L
133984 22801 135583 19909 136618 17004 c
137500 14499 137941 11883 137941 9149 c
137941 6044 137347 3052 136159 153 c
134971 -2740 133525 -5268 131826 -7426 C
@c
F
T
@rax 245.74762 489.17339 250.21757 497.79921 @E
[0.00028346 0.00000000 0.00000000 0.00028346 245.74761306 489.17336793] @tm
0 O 0 @g
0.00 1.00 1.00 0.00 k
e
% FontChange:/_ArialMT 42333.00000 z
%CHAR: 0 0 (1) @t
/$fm 1 def
15769 0 m
12051 0 L
12051 23706 L
11155 22853 9984 21999 8530 21145 c
7070 20292 5764 19650 4607 19226 C
4607 22817 L
6689 23798 8509 24984 10068 26373 c
11620 27770 12728 29118 13377 30430 C
15769 30430 L
15769 0 L
@c
F
T
@rax 323.71285 488.79354 329.75490 497.41937 @E
[0.00028346 0.00000000 0.00000000 0.00028346 323.71283855 488.79352543] @tm
0 O 0 @g
0.00 1.00 1.00 0.00 k
e
% FontChange:/_ArialMT 42333.00000 z
%CHAR: 0 0 (2) @t
/$fm 1 def
21315 3577 m
21315 0 L
1284 0 L
1256 896 1397 1757 1714 2582 c
2222 3951 3041 5292 4163 6618 c
5292 7937 6914 9468 9031 11204 c
12326 13906 14555 16044 15713 17625 c
16870 19198 17448 20694 17448 22098 c
17448 23572 16919 24814 15868 25830 c
14809 26839 13434 27347 11740 27347 c
9948 27347 8516 26811 7444 25731 c
6364 24659 5821 23170 5807 21272 C
1983 21660 L
2244 24518 3231 26691 4939 28187 c
6646 29682 8946 30430 11825 30430 c
14732 30430 17032 29619 18725 28010 c
20426 26395 21272 24398 21272 22013 c
21272 20800 21025 19607 20524 18436 c
20031 17265 19205 16030 18055 14739 c
16905 13441 14993 11663 12319 9405 c
10089 7528 8657 6258 8022 5588 c
7387 4925 6865 4254 6449 3577 C
21315 3577 L
@c
F
T
@rax 418.03087 487.58428 425.71276 496.17411 @E
[0.00028346 0.00000000 0.00000000 0.00028346 418.03085085 487.58426563] @tm
0 O 0 @g
0.00 1.00 1.00 0.00 k
e
% FontChange:/_ArialMT 42333.00000 z
%CHAR: 0 0 (N) @t
/$fm 1 def
3224 0 m
3224 30303 L
7338 30303 L
23255 6512 L
23255 30303 L
27100 30303 L
27100 0 L
22987 0 L
7070 23812 L
7070 0 L
3224 0 L
@c
F
T
@rax 131.44195 516.37266 147.25531 524.99282 @E
[0.00028346 0.00000000 0.00000000 0.00028346 131.44194795 518.37106765] @tm
0 O 0 @g
0.00 0.00 0.00 1.00 k
e
% FontChange:/_TimesNewRomanPSMT 35278.00000 z
%CHAR: 0 0 (T) @t
/$fm 1 def
20414 23360 m
20673 17880 L
20014 17880 L
19891 18844 19720 19532 19497 19950 c
19144 20614 18668 21102 18080 21420 c
17486 21737 16710 21896 15746 21896 c
12453 21896 L
12453 4045 l
12453 2611 12612 1717 12918 1358 c
13353 876 14029 635 14934 635 c
15746 635 L
15746 0 L
5839 0 L
5839 635 L
6668 635 l
7655 635 8355 935 8767 1535 c
9019 1899 9149 2740 9149 4045 c
9149 21896 L
6338 21896 l
5251 21896 4474 21814 4016 21655 c
3416 21437 2905 21014 2481 20397 c
2058 19773 1805 18938 1723 17880 C
1070 17880 L
1346 23360 L
20414 23360 L
@c
F
T
@rax 131.44195 516.37266 147.25531 524.99282 @E
[0.00028346 0.00000000 0.00000000 0.00028346 131.44194795 518.37106765] @tm
0 O 0 @g
0.00 0.00 0.00 1.00 k
e
T
@rax 131.44195 516.37266 147.25531 524.99282 @E
[0.00028346 0.00000000 0.00000000 0.00028346 131.44194795 518.37106765] @tm
0 O 0 @g
0.00 0.00 0.00 1.00 k
e
% FontChange:/_TimesNewRomanPSMT 17639.00000 z
%CHAR: 21549 -7056 (D) @t
/$fm 1 def
21852 -7056 m
21852 -6738 L
22290 -6738 l
22784 -6738 23134 -6580 23339 -6262 c
23466 -6074 23530 -5648 23530 -4989 c
23530 2557 l
23530 3286 23448 3742 23289 3924 c
23066 4177 22731 4304 22290 4304 c
21852 4304 L
21852 4624 L
26606 4624 l
28349 4624 29678 4424 30589 4030 c
31497 3633 32229 2972 32785 2049 c
33338 1123 33617 55 33617 -1156 c
33617 -2781 33123 -4140 32135 -5230 c
31027 -6447 29337 -7056 27070 -7056 c
21852 -7056 L
@c
25183 -6212 m
25912 -6374 26523 -6453 27017 -6453 c
28349 -6453 29454 -5986 30333 -5048 c
31212 -4113 31653 -2843 31653 -1241 c
31653 370 31212 1643 30333 2572 c
29454 3504 28328 3968 26949 3968 c
26432 3968 25844 3886 25183 3718 C
25183 -6212 L
@c
F
%CHAR: 34290 -7056 (L) @t
/$fm 1 def
44400 -3825 m
44685 -3887 L
43686 -7056 L
34643 -7056 L
34643 -6738 L
35084 -6738 l
35578 -6738 35930 -6577 36142 -6256 c
36263 -6071 36321 -5645 36321 -4980 c
36321 2557 l
36321 3286 36242 3742 36080 3924 c
35857 4177 35525 4304 35084 4304 c
34643 4304 L
34643 4624 L
39932 4624 L
39932 4304 L
39311 4309 38876 4254 38626 4133 c
38376 4012 38206 3860 38115 3674 c
38024 3492 37977 3051 37977 2357 c
37977 -4980 l
37977 -5457 38024 -5783 38115 -5962 c
38182 -6083 38288 -6171 38432 -6230 c
38576 -6286 39023 -6315 39776 -6315 c
40628 -6315 l
41525 -6315 42154 -6250 42516 -6118 c
42877 -5986 43207 -5751 43506 -5416 c
43803 -5080 44103 -4548 44400 -3825 C
@c
F
%CHAR: 43303 -7056 (Y) @t
/$fm 1 def
51708 4624 m
55783 4624 L
55783 4304 L
55559 4304 l
55409 4304 55192 4239 54904 4107 c
54618 3974 54357 3786 54122 3539 c
53886 3292 53595 2889 53251 2331 c
50435 -2102 L
50435 -5033 l
50435 -5751 50514 -6198 50676 -6377 c
50894 -6618 51238 -6738 51708 -6738 c
52087 -6738 L
52087 -7056 L
47128 -7056 L
47128 -6738 L
47539 -6738 l
48033 -6738 48386 -6589 48592 -6289 c
48718 -6106 48780 -5686 48780 -5033 c
48780 -2267 L
45575 2625 l
45199 3198 44940 3557 44805 3701 c
44670 3845 44391 4018 43967 4218 c
43853 4277 43685 4304 43468 4304 C
43468 4624 L
48462 4624 L
48462 4304 L
48204 4304 l
47933 4304 47686 4242 47460 4115 c
47231 3989 47119 3798 47119 3545 c
47119 3339 47292 2966 47645 2428 c
50082 -1338 L
52372 2263 l
52716 2804 52890 3204 52890 3469 c
52890 3630 52849 3774 52763 3901 c
52681 4027 52561 4124 52408 4198 c
52252 4268 52020 4304 51708 4304 C
51708 4624 L
@c
F
T
@rax 131.44195 516.37266 147.25531 524.99282 @E
[0.00028346 0.00000000 0.00000000 0.00028346 131.44194795 518.37106765] @tm
0 O 0 @g
0.00 0.00 0.00 1.00 k
e
T
@rax 189.37474 530.15386 207.16441 538.16882 @E
[0.00028346 0.00000000 0.00000000 0.00028346 189.37473323 532.22709864] @tm
0 O 0 @g
0.00 0.00 0.00 1.00 k
e
% FontChange:/_TimesNewRomanPSMT 35278.00000 z
%CHAR: 0 0 (t) @t
/$fm 1 def
5686 20961 m
5686 15781 L
9372 15781 L
9372 14576 L
5686 14576 L
5686 4339 l
5686 3316 5833 2628 6127 2275 c
6415 1917 6791 1740 7250 1740 c
7632 1740 7996 1858 8355 2093 c
8708 2328 8984 2675 9184 3134 C
9854 3134 L
9449 2011 8884 1164 8149 594 c
7414 24 6656 -259 5874 -259 c
5345 -259 4827 -112 4322 182 c
3816 476 3445 894 3204 1441 c
2963 1981 2840 2822 2840 3963 c
2840 14576 L
347 14576 L
347 15140 L
976 15393 1623 15822 2281 16422 c
2940 17028 3534 17745 4045 18568 c
4310 19003 4680 19803 5151 20961 C
5686 20961 L
@c
F
T
@rax 189.37474 530.15386 207.16441 538.16882 @E
[0.00028346 0.00000000 0.00000000 0.00028346 189.37473323 532.22709864] @tm
0 O 0 @g
0.00 0.00 0.00 1.00 k
e
T
@rax 189.37474 530.15386 207.16441 538.16882 @E
[0.00028346 0.00000000 0.00000000 0.00028346 189.37473323 532.22709864] @tm
0 O 0 @g
0.00 0.00 0.00 1.00 k
e
% FontChange:/_TimesNewRomanPSMT 17639.00000 z
%CHAR: 9801 -7056 (S) @t
/$fm 1 def
17888 4889 m
17888 849 L
17571 849 L
17465 1625 17280 2243 17015 2701 c
16748 3163 16366 3527 15872 3795 c
15378 4065 14869 4201 14340 4201 c
13743 4201 13249 4018 12858 3654 c
12467 3289 12273 2875 12273 2410 c
12273 2055 12397 1728 12644 1437 c
13000 1005 13846 432 15184 -286 c
16275 -871 17021 -1320 17418 -1635 c
17818 -1947 18127 -2317 18341 -2740 c
18556 -3167 18665 -3611 18665 -4075 c
18665 -4960 18321 -5721 17636 -6362 c
16948 -7003 16066 -7324 14987 -7324 c
14646 -7324 14328 -7297 14028 -7244 c
13852 -7218 13482 -7112 12923 -6933 c
12365 -6750 12009 -6659 11859 -6659 c
11715 -6659 11603 -6703 11518 -6788 c
11436 -6874 11374 -7053 11333 -7324 C
11015 -7324 L
11015 -3319 L
11333 -3319 L
11483 -4157 11685 -4784 11938 -5201 c
12191 -5615 12576 -5962 13097 -6239 c
13614 -6512 14184 -6650 14805 -6650 c
15522 -6650 16089 -6462 16507 -6083 c
16921 -5704 17130 -5257 17130 -4739 c
17130 -4451 17051 -4163 16895 -3869 c
16736 -3575 16489 -3305 16157 -3052 c
15933 -2878 15322 -2514 14322 -1952 c
13323 -1394 12614 -947 12191 -615 c
11768 -280 11450 88 11230 488 c
11012 890 10903 1334 10903 1816 c
10903 2654 11224 3375 11868 3980 c
12512 4586 13329 4889 14322 4889 c
14943 4889 15601 4739 16295 4433 c
16616 4289 16845 4218 16974 4218 c
17124 4218 17248 4262 17342 4351 c
17436 4442 17512 4621 17571 4889 C
17888 4889 L
@c
F
%CHAR: 19608 -7056 (T) @t
/$fm 1 def
29815 4624 m
29944 1884 L
29615 1884 L
29553 2366 29468 2710 29356 2919 c
29180 3251 28942 3495 28648 3654 c
28351 3813 27963 3892 27481 3892 c
25835 3892 L
25835 -5033 l
25835 -5751 25914 -6198 26067 -6377 c
26284 -6618 26622 -6738 27075 -6738 c
27481 -6738 L
27481 -7056 L
22527 -7056 L
22527 -6738 L
22942 -6738 l
23436 -6738 23786 -6589 23991 -6289 c
24118 -6106 24182 -5686 24182 -5033 c
24182 3892 L
22777 3892 l
22233 3892 21845 3851 21616 3771 c
21316 3663 21060 3451 20849 3142 c
20637 2831 20511 2413 20469 1884 C
20143 1884 L
20281 4624 L
29815 4624 L
@c
F
%CHAR: 28974 -7056 (A) @t
/$fm 1 def
37044 -3146 m
32522 -3146 L
31732 -4989 l
31535 -5442 31438 -5780 31438 -6006 c
31438 -6183 31523 -6339 31690 -6474 c
31861 -6609 32225 -6697 32790 -6738 C
32790 -7056 L
29112 -7056 L
29112 -6738 L
29600 -6650 29915 -6539 30059 -6400 c
30353 -6127 30676 -5566 31032 -4722 c
35142 4889 L
35442 4889 L
39507 -4825 l
39834 -5607 40131 -6112 40398 -6345 c
40666 -6577 41039 -6709 41515 -6738 C
41515 -7056 L
36906 -7056 L
36906 -6738 L
37370 -6715 37685 -6636 37849 -6503 c
38014 -6374 38096 -6212 38096 -6021 c
38096 -5768 37979 -5371 37749 -4825 c
37044 -3146 L
@c
36803 -2508 m
34821 2210 L
32790 -2508 L
36803 -2508 L
@c
F
%CHAR: 41715 -7056 (R) @t
/$fm 1 def
53636 -7056 m
50517 -7056 L
46563 -1597 L
46272 -1606 46034 -1611 45848 -1611 c
45775 -1611 45696 -1611 45607 -1608 c
45522 -1606 45434 -1603 45340 -1597 C
45340 -4989 l
45340 -5724 45422 -6180 45581 -6359 c
45801 -6612 46128 -6738 46563 -6738 c
47021 -6738 L
47021 -7056 L
42018 -7056 L
42018 -6738 L
42456 -6738 l
42950 -6738 43303 -6577 43514 -6256 c
43635 -6077 43696 -5654 43696 -4989 c
43696 2557 l
43696 3292 43614 3748 43455 3924 c
43232 4177 42897 4304 42456 4304 c
42018 4304 L
42018 4624 L
46272 4624 l
47512 4624 48427 4533 49015 4351 c
49603 4171 50102 3839 50514 3354 c
50923 2866 51128 2290 51128 1617 c
51128 899 50896 276 50426 -253 c
49958 -779 49235 -1153 48253 -1370 C
50664 -4722 l
51214 -5492 51690 -6004 52084 -6256 c
52481 -6506 52998 -6668 53636 -6738 C
53636 -7056 L
@c
45340 -1053 m
45452 -1053 45546 -1053 45625 -1056 c
45704 -1059 45772 -1062 45822 -1062 c
46936 -1062 47777 -821 48341 -338 c
48909 144 49191 758 49191 1505 c
49191 2234 48962 2828 48506 3283 c
48050 3739 47445 3968 46692 3968 c
46360 3968 45910 3913 45340 3804 C
45340 -1053 L
@c
F
%CHAR: 52422 -7056 (T) @t
/$fm 1 def
62629 4624 m
62758 1884 L
62429 1884 L
62367 2366 62282 2710 62170 2919 c
61994 3251 61756 3495 61462 3654 c
61165 3813 60777 3892 60295 3892 c
58649 3892 L
58649 -5033 l
58649 -5751 58728 -6198 58881 -6377 c
59098 -6618 59436 -6738 59889 -6738 c
60295 -6738 L
60295 -7056 L
55341 -7056 L
55341 -6738 L
55756 -6738 l
56250 -6738 56600 -6589 56805 -6289 c
56932 -6106 56996 -5686 56996 -5033 c
56996 3892 L
55591 3892 l
55047 3892 54659 3851 54430 3771 c
54130 3663 53874 3451 53663 3142 c
53451 2831 53325 2413 53283 1884 C
52957 1884 L
53095 4624 L
62629 4624 L
@c
F
T
@rax 189.37474 530.15386 207.16441 538.16882 @E
[0.00028346 0.00000000 0.00000000 0.00028346 189.37473323 532.22709864] @tm
0 O 0 @g
0.00 0.00 0.00 1.00 k
e
T
@rax 64.85301 483.97493 73.97631 491.98989 @E
[0.00028346 0.00000000 0.00000000 0.00028346 64.85300550 486.04817120] @tm
0 O 0 @g
0.00 0.00 0.00 1.00 k
e
% FontChange:/_TimesNewRomanPSMT 35278.00000 z
%CHAR: 0 0 (t) @t
/$fm 1 def
5686 20961 m
5686 15781 L
9372 15781 L
9372 14576 L
5686 14576 L
5686 4339 l
5686 3316 5833 2628 6127 2275 c
6415 1917 6791 1740 7250 1740 c
7632 1740 7996 1858 8355 2093 c
8708 2328 8984 2675 9184 3134 C
9854 3134 L
9449 2011 8884 1164 8149 594 c
7414 24 6656 -259 5874 -259 c
5345 -259 4827 -112 4322 182 c
3816 476 3445 894 3204 1441 c
2963 1981 2840 2822 2840 3963 c
2840 14576 L
347 14576 L
347 15140 L
976 15393 1623 15822 2281 16422 c
2940 17028 3534 17745 4045 18568 c
4310 19003 4680 19803 5151 20961 C
5686 20961 L
@c
F
T
@rax 64.85301 483.97493 73.97631 491.98989 @E
[0.00028346 0.00000000 0.00000000 0.00028346 64.85300550 486.04817120] @tm
0 O 0 @g
0.00 0.00 0.00 1.00 k
e
T
@rax 64.85301 483.97493 73.97631 491.98989 @E
[0.00028346 0.00000000 0.00000000 0.00028346 64.85300550 486.04817120] @tm
0 O 0 @g
0.00 0.00 0.00 1.00 k
e
% FontChange:/_TimesNewRomanPSMT 17639.00000 z
%CHAR: 9801 -7056 (I) @t
/$fm 1 def
15246 -6738 m
15246 -7056 L
10239 -7056 L
10239 -6738 L
10654 -6738 l
11136 -6738 11486 -6597 11703 -6315 c
11841 -6133 11912 -5689 11912 -4989 c
11912 2557 l
11912 3148 11874 3539 11800 3727 c
11741 3871 11624 3995 11447 4098 c
11194 4236 10930 4304 10654 4304 c
10239 4304 L
10239 4624 L
15246 4624 L
15246 4304 L
14822 4304 l
14346 4304 13999 4162 13782 3883 c
13637 3698 13564 3257 13564 2557 c
13564 -4989 l
13564 -5580 13602 -5971 13676 -6159 c
13734 -6303 13855 -6427 14037 -6530 c
14284 -6668 14546 -6738 14822 -6738 c
15246 -6738 L
@c
F
%CHAR: 15675 -7056 (W) @t
/$fm 1 def
32185 4624 m
32185 4304 L
31882 4304 31635 4251 31444 4142 c
31256 4030 31074 3827 30903 3530 c
30789 3327 30606 2848 30359 2090 c
27105 -7324 L
26761 -7324 L
24098 144 L
21455 -7324 L
21143 -7324 L
17674 2375 l
17415 3098 17251 3527 17183 3660 c
17068 3877 16910 4039 16713 4145 c
16516 4251 16245 4304 15907 4304 C
15907 4624 L
20232 4624 L
20232 4304 L
20026 4304 l
19720 4304 19488 4236 19326 4098 c
19168 3960 19085 3792 19085 3598 c
19085 3398 19212 2934 19464 2210 c
21763 -4343 L
23701 1228 L
23357 2210 L
23083 2995 l
22963 3283 22828 3536 22678 3754 c
22601 3863 22510 3954 22401 4030 c
22257 4133 22113 4207 21972 4254 c
21860 4286 21690 4304 21455 4304 C
21455 4624 L
26003 4624 L
26003 4304 L
25691 4304 l
25371 4304 25135 4236 24985 4098 c
24836 3960 24762 3774 24762 3539 c
24762 3245 24891 2734 25150 2005 c
27387 -4343 L
29610 2090 l
29863 2804 29989 3295 29989 3571 c
29989 3704 29948 3827 29866 3942 c
29780 4057 29677 4139 29551 4183 c
29333 4265 29048 4304 28698 4304 C
28698 4624 L
32185 4624 L
@c
F
T
@rax 64.85301 483.97493 73.97631 491.98989 @E
[0.00028346 0.00000000 0.00000000 0.00028346 64.85300550 486.04817120] @tm
0 O 0 @g
0.00 0.00 0.00 1.00 k
e
T
@rax 261.10772 517.25792 272.60107 525.95291 @E
[0.00028346 0.00000000 0.00000000 0.00028346 261.10770698 519.33116211] @tm
0 O 0 @g
0.00 0.00 0.00 1.00 k
e
% FontChange:/_TimesNewRomanPSMT 35278.00000 z
%CHAR: 0 0 (T) @t
/$fm 1 def
20414 23360 m
20673 17880 L
20014 17880 L
19891 18844 19720 19532 19497 19950 c
19144 20614 18668 21102 18080 21420 c
17486 21737 16710 21896 15746 21896 c
12453 21896 L
12453 4045 l
12453 2611 12612 1717 12918 1358 c
13353 876 14029 635 14934 635 c
15746 635 L
15746 0 L
5839 0 L
5839 635 L
6668 635 l
7655 635 8355 935 8767 1535 c
9019 1899 9149 2740 9149 4045 c
9149 21896 L
6338 21896 l
5251 21896 4474 21814 4016 21655 c
3416 21437 2905 21014 2481 20397 c
2058 19773 1805 18938 1723 17880 C
1070 17880 L
1346 23360 L
20414 23360 L
@c
F
T
@rax 261.10772 517.25792 272.60107 525.95291 @E
[0.00028346 0.00000000 0.00000000 0.00028346 261.10770698 519.33116211] @tm
0 O 0 @g
0.00 0.00 0.00 1.00 k
e
T
@rax 261.10772 517.25792 272.60107 525.95291 @E
[0.00028346 0.00000000 0.00000000 0.00028346 261.10770698 519.33116211] @tm
0 O 0 @g
0.00 0.00 0.00 1.00 k
e
% FontChange:/_TimesNewRomanPSMT 17639.00000 z
%CHAR: 21549 -7056 (S) @t
/$fm 1 def
29636 4889 m
29636 849 L
29319 849 L
29213 1625 29028 2243 28763 2701 c
28496 3163 28114 3527 27620 3795 c
27126 4065 26617 4201 26088 4201 c
25491 4201 24997 4018 24606 3654 c
24215 3289 24021 2875 24021 2410 c
24021 2055 24145 1728 24392 1437 c
24748 1005 25594 432 26932 -286 c
28023 -871 28769 -1320 29166 -1635 c
29566 -1947 29875 -2317 30089 -2740 c
30304 -3167 30413 -3611 30413 -4075 c
30413 -4960 30069 -5721 29384 -6362 c
28696 -7003 27814 -7324 26735 -7324 c
26394 -7324 26076 -7297 25776 -7244 c
25600 -7218 25230 -7112 24671 -6933 c
24113 -6750 23757 -6659 23607 -6659 c
23463 -6659 23351 -6703 23266 -6788 c
23184 -6874 23122 -7053 23081 -7324 C
22763 -7324 L
22763 -3319 L
23081 -3319 L
23231 -4157 23433 -4784 23686 -5201 c
23939 -5615 24324 -5962 24845 -6239 c
25362 -6512 25932 -6650 26553 -6650 c
27270 -6650 27837 -6462 28255 -6083 c
28669 -5704 28878 -5257 28878 -4739 c
28878 -4451 28799 -4163 28643 -3869 c
28484 -3575 28237 -3305 27905 -3052 c
27681 -2878 27070 -2514 26070 -1952 c
25071 -1394 24362 -947 23939 -615 c
23516 -280 23198 88 22978 488 c
22760 890 22651 1334 22651 1816 c
22651 2654 22972 3375 23616 3980 c
24260 4586 25077 4889 26070 4889 c
26691 4889 27349 4739 28043 4433 c
28364 4289 28593 4218 28722 4218 c
28872 4218 28996 4262 29090 4351 c
29184 4442 29260 4621 29319 4889 C
29636 4889 L
@c
F
%CHAR: 31356 -7056 (P) @t
/$fm 1 def
34972 -1588 m
34972 -4989 l
34972 -5724 35054 -6180 35216 -6359 c
35434 -6612 35763 -6738 36204 -6738 c
36654 -6738 L
36654 -7056 L
31650 -7056 L
31650 -6738 L
32088 -6738 l
32582 -6738 32935 -6577 33146 -6256 c
33261 -6077 33320 -5654 33320 -4989 c
33320 2557 l
33320 3292 33243 3748 33088 3924 c
32864 4177 32529 4304 32088 4304 c
31650 4304 L
31650 4624 L
35930 4624 l
36974 4624 37797 4515 38400 4301 c
39005 4086 39511 3721 39926 3210 c
40340 2698 40546 2093 40546 1393 c
40546 441 40231 -336 39602 -932 c
38973 -1529 38085 -1829 36936 -1829 c
36657 -1829 36351 -1808 36024 -1767 c
35698 -1726 35345 -1667 34972 -1588 C
@c
34972 -1097 m
35278 -1153 35548 -1197 35783 -1226 c
36019 -1253 36218 -1267 36386 -1267 c
36983 -1267 37497 -1038 37932 -574 c
38365 -112 38582 485 38582 1220 c
38582 1725 38479 2196 38273 2628 c
38065 3063 37774 3386 37394 3601 c
37015 3818 36583 3924 36101 3924 c
35810 3924 35434 3871 34972 3763 C
34972 -1097 L
@c
F
T
@rax 261.10772 517.25792 272.60107 525.95291 @E
[0.00028346 0.00000000 0.00000000 0.00028346 261.10770698 519.33116211] @tm
0 O 0 @g
0.00 0.00 0.00 1.00 k
e
T
@rax 320.43855 517.13036 334.00687 525.82535 @E
[0.00028346 0.00000000 0.00000000 0.00028346 320.43853946 519.20360305] @tm
0 O 0 @g
0.00 0.00 0.00 1.00 k
e
% FontChange:/_TimesNewRomanPSMT 35278.00000 z
%CHAR: 0 0 (T) @t
/$fm 1 def
20414 23360 m
20673 17880 L
20014 17880 L
19891 18844 19720 19532 19497 19950 c
19144 20614 18668 21102 18080 21420 c
17486 21737 16710 21896 15746 21896 c
12453 21896 L
12453 4045 l
12453 2611 12612 1717 12918 1358 c
13353 876 14029 635 14934 635 c
15746 635 L
15746 0 L
5839 0 L
5839 635 L
6668 635 l
7655 635 8355 935 8767 1535 c
9019 1899 9149 2740 9149 4045 c
9149 21896 L
6338 21896 l
5251 21896 4474 21814 4016 21655 c
3416 21437 2905 21014 2481 20397 c
2058 19773 1805 18938 1723 17880 C
1070 17880 L
1346 23360 L
20414 23360 L
@c
F
T
@rax 320.43855 517.13036 334.00687 525.82535 @E
[0.00028346 0.00000000 0.00000000 0.00028346 320.43853946 519.20360305] @tm
0 O 0 @g
0.00 0.00 0.00 1.00 k
e
T
@rax 320.43855 517.13036 334.00687 525.82535 @E
[0.00028346 0.00000000 0.00000000 0.00028346 320.43853946 519.20360305] @tm
0 O 0 @g
0.00 0.00 0.00 1.00 k
e
% FontChange:/_TimesNewRomanPSMT 17639.00000 z
%CHAR: 21549 -7056 (P) @t
/$fm 1 def
25165 -1588 m
25165 -4989 l
25165 -5724 25247 -6180 25409 -6359 c
25627 -6612 25956 -6738 26397 -6738 c
26847 -6738 L
26847 -7056 L
21843 -7056 L
21843 -6738 L
22281 -6738 l
22775 -6738 23128 -6577 23339 -6256 c
23454 -6077 23513 -5654 23513 -4989 c
23513 2557 l
23513 3292 23436 3748 23281 3924 c
23057 4177 22722 4304 22281 4304 c
21843 4304 L
21843 4624 L
26123 4624 l
27167 4624 27990 4515 28593 4301 c
29198 4086 29704 3721 30119 3210 c
30533 2698 30739 2093 30739 1393 c
30739 441 30424 -336 29795 -932 c
29166 -1529 28278 -1829 27129 -1829 c
26850 -1829 26544 -1808 26217 -1767 c
25891 -1726 25538 -1667 25165 -1588 C
@c
25165 -1097 m
25471 -1153 25741 -1197 25976 -1226 c
26212 -1253 26411 -1267 26579 -1267 c
27176 -1267 27690 -1038 28125 -574 c
28558 -112 28775 485 28775 1220 c
28775 1725 28672 2196 28466 2628 c
28258 3063 27967 3386 27587 3601 c
27208 3818 26776 3924 26294 3924 c
26003 3924 25627 3871 25165 3763 C
25165 -1097 L
@c
F
%CHAR: 31356 -7056 (W) @t
/$fm 1 def
47866 4624 m
47866 4304 L
47563 4304 47316 4251 47125 4142 c
46937 4030 46755 3827 46584 3530 c
46470 3327 46287 2848 46040 2090 c
42786 -7324 L
42442 -7324 L
39779 144 L
37136 -7324 L
36824 -7324 L
33355 2375 l
33096 3098 32932 3527 32864 3660 c
32749 3877 32591 4039 32394 4145 c
32197 4251 31926 4304 31588 4304 C
31588 4624 L
35913 4624 L
35913 4304 L
35707 4304 l
35401 4304 35169 4236 35007 4098 c
34849 3960 34766 3792 34766 3598 c
34766 3398 34893 2934 35145 2210 c
37444 -4343 L
39382 1228 L
39038 2210 L
38764 2995 l
38644 3283 38509 3536 38359 3754 c
38282 3863 38191 3954 38082 4030 c
37938 4133 37794 4207 37653 4254 c
37541 4286 37371 4304 37136 4304 C
37136 4624 L
41684 4624 L
41684 4304 L
41372 4304 l
41052 4304 40816 4236 40666 4098 c
40517 3960 40443 3774 40443 3539 c
40443 3245 40572 2734 40831 2005 c
43068 -4343 L
45291 2090 l
45544 2804 45670 3295 45670 3571 c
45670 3704 45629 3827 45547 3942 c
45461 4057 45358 4139 45232 4183 c
45014 4265 44729 4304 44379 4304 C
44379 4624 L
47866 4624 L
@c
F
T
@rax 320.43855 517.13036 334.00687 525.82535 @E
[0.00028346 0.00000000 0.00000000 0.00028346 320.43853946 519.20360305] @tm
0 O 0 @g
0.00 0.00 0.00 1.00 k
e
T
@rax 458.55950 517.25055 470.19430 525.87071 @E
[0.00028346 0.00000000 0.00000000 0.00028346 458.55947929 519.24895738] @tm
0 O 0 @g
0.00 0.00 0.00 1.00 k
e
% FontChange:/_TimesNewRomanPSMT 35278.00000 z
%CHAR: 0 0 (T) @t
/$fm 1 def
20414 23360 m
20673 17880 L
20014 17880 L
19891 18844 19720 19532 19497 19950 c
19144 20614 18668 21102 18080 21420 c
17486 21737 16710 21896 15746 21896 c
12453 21896 L
12453 4045 l
12453 2611 12612 1717 12918 1358 c
13353 876 14029 635 14934 635 c
15746 635 L
15746 0 L
5839 0 L
5839 635 L
6668 635 l
7655 635 8355 935 8767 1535 c
9019 1899 9149 2740 9149 4045 c
9149 21896 L
6338 21896 l
5251 21896 4474 21814 4016 21655 c
3416 21437 2905 21014 2481 20397 c
2058 19773 1805 18938 1723 17880 C
1070 17880 L
1346 23360 L
20414 23360 L
@c
F
T
@rax 458.55950 517.25055 470.19430 525.87071 @E
[0.00028346 0.00000000 0.00000000 0.00028346 458.55947929 519.24895738] @tm
0 O 0 @g
0.00 0.00 0.00 1.00 k
e
T
@rax 458.55950 517.25055 470.19430 525.87071 @E
[0.00028346 0.00000000 0.00000000 0.00028346 458.55947929 519.24895738] @tm
0 O 0 @g
0.00 0.00 0.00 1.00 k
e
% FontChange:/_TimesNewRomanPSMT 17639.00000 z
%CHAR: 21549 -7056 (L) @t
/$fm 1 def
31659 -3825 m
31944 -3887 L
30945 -7056 L
21902 -7056 L
21902 -6738 L
22343 -6738 l
22837 -6738 23189 -6577 23401 -6256 c
23522 -6071 23580 -5645 23580 -4980 c
23580 2557 l
23580 3286 23501 3742 23339 3924 c
23116 4177 22784 4304 22343 4304 c
21902 4304 L
21902 4624 L
27191 4624 L
27191 4304 L
26570 4309 26135 4254 25885 4133 c
25635 4012 25465 3860 25374 3674 c
25283 3492 25236 3051 25236 2357 c
25236 -4980 l
25236 -5457 25283 -5783 25374 -5962 c
25441 -6083 25547 -6171 25691 -6230 c
25835 -6286 26282 -6315 27035 -6315 c
27887 -6315 l
28784 -6315 29413 -6250 29775 -6118 c
30136 -5986 30466 -5751 30765 -5416 c
31062 -5080 31362 -4548 31659 -3825 C
@c
F
%CHAR: 30709 -7056 (T) @t
/$fm 1 def
40916 4624 m
41045 1884 L
40716 1884 L
40654 2366 40569 2710 40457 2919 c
40281 3251 40043 3495 39749 3654 c
39452 3813 39064 3892 38582 3892 c
36936 3892 L
36936 -5033 l
36936 -5751 37015 -6198 37168 -6377 c
37385 -6618 37723 -6738 38176 -6738 c
38582 -6738 L
38582 -7056 L
33628 -7056 L
33628 -6738 L
34043 -6738 l
34537 -6738 34887 -6589 35092 -6289 c
35219 -6106 35283 -5686 35283 -5033 c
35283 3892 L
33878 3892 l
33334 3892 32946 3851 32717 3771 c
32417 3663 32161 3451 31950 3142 c
31738 2831 31612 2413 31570 1884 C
31244 1884 L
31382 4624 L
40916 4624 L
@c
F
T
@rax 458.55950 517.25055 470.19430 525.87071 @E
[0.00028346 0.00000000 0.00000000 0.00028346 458.55947929 519.24895738] @tm
0 O 0 @g
0.00 0.00 0.00 1.00 k
e
T
@rax 279.47849 465.30057 295.41515 473.99556 @E
[0.00028346 0.00000000 0.00000000 0.00028346 279.47847797 467.37380967] @tm
0 O 0 @g
0.00 0.00 0.00 1.00 k
e
% FontChange:/_TimesNewRomanPSMT 35278.00000 z
%CHAR: 0 0 (T) @t
/$fm 1 def
20414 23360 m
20673 17880 L
20014 17880 L
19891 18844 19720 19532 19497 19950 c
19144 20614 18668 21102 18080 21420 c
17486 21737 16710 21896 15746 21896 c
12453 21896 L
12453 4045 l
12453 2611 12612 1717 12918 1358 c
13353 876 14029 635 14934 635 c
15746 635 L
15746 0 L
5839 0 L
5839 635 L
6668 635 l
7655 635 8355 935 8767 1535 c
9019 1899 9149 2740 9149 4045 c
9149 21896 L
6338 21896 l
5251 21896 4474 21814 4016 21655 c
3416 21437 2905 21014 2481 20397 c
2058 19773 1805 18938 1723 17880 C
1070 17880 L
1346 23360 L
20414 23360 L
@c
F
T
@rax 279.47849 465.30057 295.41515 473.99556 @E
[0.00028346 0.00000000 0.00000000 0.00028346 279.47847797 467.37380967] @tm
0 O 0 @g
0.00 0.00 0.00 1.00 k
e
T
@rax 279.47849 465.30057 295.41515 473.99556 @E
[0.00028346 0.00000000 0.00000000 0.00028346 279.47847797 467.37380967] @tm
0 O 0 @g
0.00 0.00 0.00 1.00 k
e
% FontChange:/_TimesNewRomanPSMT 17639.00000 z
%CHAR: 21549 -7056 (G) @t
/$fm 1 def
32376 4889 m
32676 1205 L
32376 1205 L
32071 2122 31677 2813 31195 3280 c
30501 3951 29607 4286 28516 4286 c
27029 4286 25897 3698 25124 2522 c
24474 1528 24151 347 24151 -1026 c
24151 -2141 24365 -3158 24795 -4075 c
25227 -4995 25791 -5668 26488 -6095 c
27185 -6524 27902 -6738 28637 -6738 c
29069 -6738 29484 -6683 29886 -6574 c
30289 -6465 30674 -6303 31048 -6092 C
31048 -2714 l
31048 -2129 31004 -1747 30915 -1564 c
30827 -1385 30689 -1247 30501 -1153 c
30316 -1056 29986 -1009 29516 -1009 C
29516 -682 L
34037 -682 L
34037 -1009 L
33823 -1009 l
33373 -1009 33067 -1159 32900 -1459 c
32785 -1670 32729 -2091 32729 -2714 c
32729 -6289 L
32068 -6644 31415 -6906 30774 -7074 c
30130 -7241 29416 -7324 28628 -7324 c
26373 -7324 24659 -6600 23486 -5154 c
22607 -4066 22169 -2817 22169 -1397 c
22169 -371 22416 614 22910 1558 c
23495 2678 24301 3539 25321 4142 c
26176 4642 27188 4889 28352 4889 c
28778 4889 29163 4856 29513 4786 c
29860 4718 30351 4565 30989 4330 c
31309 4209 31527 4151 31636 4151 c
31744 4151 31838 4201 31915 4301 c
31991 4401 32038 4597 32056 4889 C
32376 4889 L
@c
F
%CHAR: 34290 -7056 (A) @t
/$fm 1 def
42360 -3146 m
37838 -3146 L
37048 -4989 l
36851 -5442 36754 -5780 36754 -6006 c
36754 -6183 36839 -6339 37006 -6474 c
37177 -6609 37541 -6697 38106 -6738 C
38106 -7056 L
34428 -7056 L
34428 -6738 L
34916 -6650 35231 -6539 35375 -6400 c
35669 -6127 35992 -5566 36348 -4722 c
40458 4889 L
40758 4889 L
44823 -4825 l
45150 -5607 45447 -6112 45714 -6345 c
45982 -6577 46355 -6709 46831 -6738 C
46831 -7056 L
42222 -7056 L
42222 -6738 L
42686 -6715 43001 -6636 43165 -6503 c
43330 -6374 43412 -6212 43412 -6021 c
43412 -5768 43295 -5371 43065 -4825 c
42360 -3146 L
@c
42119 -2508 m
40137 2210 L
38106 -2508 L
42119 -2508 L
@c
F
%CHAR: 47031 -7056 (P) @t
/$fm 1 def
50647 -1588 m
50647 -4989 l
50647 -5724 50729 -6180 50891 -6359 c
51109 -6612 51438 -6738 51879 -6738 c
52329 -6738 L
52329 -7056 L
47325 -7056 L
47325 -6738 L
47763 -6738 l
48257 -6738 48610 -6577 48821 -6256 c
48936 -6077 48995 -5654 48995 -4989 c
48995 2557 l
48995 3292 48918 3748 48763 3924 c
48539 4177 48204 4304 47763 4304 c
47325 4304 L
47325 4624 L
51605 4624 l
52649 4624 53472 4515 54075 4301 c
54680 4086 55186 3721 55601 3210 c
56015 2698 56221 2093 56221 1393 c
56221 441 55906 -336 55277 -932 c
54648 -1529 53760 -1829 52611 -1829 c
52332 -1829 52026 -1808 51699 -1767 c
51373 -1726 51020 -1667 50647 -1588 C
@c
50647 -1097 m
50953 -1153 51223 -1197 51458 -1226 c
51694 -1253 51893 -1267 52061 -1267 c
52658 -1267 53172 -1038 53607 -574 c
54040 -112 54257 485 54257 1220 c
54257 1725 54154 2196 53948 2628 c
53740 3063 53449 3386 53069 3601 c
52690 3818 52258 3924 51776 3924 c
51485 3924 51109 3871 50647 3763 C
50647 -1097 L
@c
F
T
@rax 279.47849 465.30057 295.41515 473.99556 @E
[0.00028346 0.00000000 0.00000000 0.00028346 279.47847797 467.37380967] @tm
0 O 0 @g
0.00 0.00 0.00 1.00 k
e
T
@rax %Note: Object
211.57172 524.08970 227.00580 531.61852 @E
0 J 0 j [] 0 d 0 R 0 @G
0.00 0.00 0.00 1.00 K
0 0.50003 0.50003 0.00000 @w
/$fm 0 def
211.57172 531.61852 m
218.50980 531.61852 226.04372 528.04233 227.00580 524.08970 C
S
@j
0.00 0.00 0.00 1.00 K
0.00 0.00 0.00 1.00 k
0 @g
0 @G
[] 0 d 0 J 0 j
0 R 0 O
0 1.00800 1.00800 0 @w
229.46740 525.11641 m
228.04016 519.83915 L
224.34718 523.87002 L
229.46740 525.11641 L
f
@J
%%PageTrailer
@rs
@rs
%%Trailer
@EndSysCorelDict
end
%%DocumentSuppliedResources: procset wCorel12Dict 12.0 0
%%EOF
Figure 2: Definition of output timings
======
Driver
======
Driver Features
===============
This driver is based on `zio`_ and it uses `fmc`_. It supports initial
setup of the board, setting and reading time, run-time continuous
calibration, input timestamping, output pulse generation and readback of
output settings from the hardware. It supports user-defined offsets,
so our users can tell the driver about channel-specific delays (for
example, to account for wiring) and ignore the issue in application code.
For each feature offered the driver (and documentation) the driver
tries to offer the following items; sometimes however one of them is missing
for a specific driver functionality, if we don't consider it important enough.
* A description of how the features works at low level;
* A low-level user-space program to test the actual mechanism;
* A C-language API to access the feature with data structures;
* An example program based on that API.
.. _drv_param:
Module Parameters
=================
The driver accepts a few load-time parameters for configuration. You
can pass them to *insmod* amd *modprobe* directly, or write them
in ``/etc/modules.conf`` or the proper file in ``/etc/modutils/``.
The following parameters are used:
verbose=
The parameter defaults to 0. If set, it enables more diagnostic
messages during probe (you may find it is not used, but it is
left in to be useful during further development, and avoid
compile-time changes like use of ``DEBUG``).
timer_ms=
The period of the internal timer, if not zero.
The timer is used to poll for input events instead of enabling
the interrupt. The default interval is 0, which means to
use interrupt support. You may want to use the timer while
porting to a different carrier, before sorting out IRQ issues.
calib_s=
The period, in seconds, of temperature measurement to re-calibrate
the output delays. Defaults to 30. If set to zero, the
re-calibration timer is not activated.
The module also supports some more parameters that are
calibration-specific. They are described in the :ref:`Calibration<dev_cal>`
section.
.. _zio: https://www.ohwr.org/project/zio
.. _fmc: https://www.ohwr.org/project/fmc-sw
Welcome to Fmc Fine-Delay Software's documentation!
===================================================
.. toctree::
:maxdepth: 2
:caption: Contents:
introduction
installation
driver
library
tools
developer-info
troubleshooting
#! /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/^[ ]*//
============
Installation
============
This driver depends on two other modules (four http://ohwr.org.
packages), as well as the Linux kernel. Also, it
must talk to a specific FPGA binary file running in the device.
Gateware Dependencies
=====================
While previous versions of this package included a gateware binary
in the ``binaries/`` subdirectory, in Jan 2014 we decided not to do
that any more. Release versions of this package are expected to
point to ``current`` gateware images for different carriers.
Clearly the driver is expected to work on any *fmc* carrier,
even those ignored to us, and we can't provide all binaries.
The up-to-date gateware binaries for the SVEC and SPEC carriers will be
always available in the *Releases* section of the Fine Delay project:
http://www.ohwr.org/projects/fmc-delay-1ns-8cha/wiki/Releases
Note that the release gateware contains a stable version of
the White Rabbit PTP Core firmware. This firmware may be reloaded dynamically
at any time using carrier-specific tools.
Gateware Installation
=====================
By default, the driver looks for a gateware file named
``/lib/firmware/fmc/[carrier]-fine-delay.bin``, where ``[carrier]`` is the
carrier's name (lowercase - currently ``svec`` or ``spec``).
To install the gateware download the bitstreams from the Release page (or build
your own, as you wish) and put them in ``/lib/firmware/fmc``. You may have
to strip the version/date attached to the file names or create symlinks.
Follow your carrier instructions to load the gateware on the FPGA.
Software Dependencies
=====================
The kernel versions I am using during development is 3.10. Everything
used here is known to build with all versions from 2.6.35 to 3.12.
The driver, then, is based on the `zio`_ framework, available from
http://ohwr.org.
The FMC mezzanine is supported by means of the `fmc`_ software project.
Both packages (`zio`_ and `fmc`_) need to be downloaded and compiled. We do not
provide submodules because their version may conflict with other projects.
It is duty of the final user to guarantee a consistent installation.
Software Installation
=====================
To install this software package, you need to tell it where your
kernel sources live, so the package can pick the right header files.
You need to set only one environment variable:
LINUX
The top-level directory of the Linux kernel you are compiling
against. If not set, the default may work if you compile in the same
host where you expect to run the driver.
Most likely, this is all you need to set. After this, you can
run:::
make
sudo make install LINUX=$LINUX
After installation, your carrier driver should load automatically
(for example, the PCI bus will load ``spec-fmc-carrier.ko``), but
``fmc-fine-delay.ko`` must be loaded manually, because support for automatic
loading is not yet in place. The suggested command is one or the other of
the following two:::
modprobe fmc-fine-delay [<parameter> ...] # after make install
insmod kernel/fmc-fine-delay.ko [<parameter> ...] # if not installed
Available module parameters are described in :ref:`Module Parameters<drv_param>`.
Unless you customized or want to customize one of the three
related packages, you can skip the rest of this section.
In order to compile *fine-delay* against a specific repository you can use
the following environment variables:
ZIO
The top-level directory of the repository checkout of each
package.
FMC
The top-level directory of the repository checkout of each
package.
Headers and dependencies for the respective package are taken from the chosen
directory.
.. _zio: https://www.ohwr.org/project/zio
.. _fmc: https://www.ohwr.org/project/fmc-sw
============
Introduction
============
This is the user manual for the *fmc-delay-1ns-4cha* board developed on
http://ohwr.org. Please note that the ohwr hardware project is
misnamed as *fmc-delay-1ns-8cha*; even if the board has 4
channels; the references to *8ch* below are thus correct, even if
the may seem wrong.
Repositories and Releases
=========================
The code and documentation is distributed in the following places:
http://www.ohwr.org/projects/fine-delay-sw/documents
This place hosts the pdf documentation for some official
release, but we prefer to use the *files* tab, below.
https://ohwr.org/project/fine-delay-sw.git
Read-only repositories for the software and documentation.
ssh://git@ohwr.org:7999/project/fine-delay-sw.git
Read-write repositories, for those authorized.
.. note::
If you got this from the repository (as opposed to a named
*tar.gz* or *pdf* file) it may happen that you are looking at a later
commit than the release this manual claims to document.
It is a fact of life that developers forget
to re-read and fix documentation while updating the code. In that case,
please run ``git describe HEAD`` to ensure where you are.
Hardware Description
====================
The *FMC Delay 1ns-4cha* is an FPGA Mezzanine Card (FMC - VITA 57 standard),
whose main purpose is to produce pulses delayed by a user-programmed value with
respect to the input trigger pulse. The card can also work as a Time to
Digital converter (TDC) or as a programmable pulse generator triggering at a
given TAI time.
For the sake of clarity of this document, the card's name will be further
abbreviated as *fine-delay*.
Requirements and Supported Platforms
====================================
*fine-delay* can work with any VITA 57-compliant FMC carrier, provided that
the carrier's FPGA has enough logic resources. The current software/gateware
release officially supports the following carrier and mezzanine combinations:
* CERN's SPEC (Simple PCI-Express Carrier) with one *fine-delay* mezzanine.
* CERN's SVEC (Simple VME64x Carrier) with one or two *fine-delay* mezzanines.
Note that if only one *fine-delay* is in use, the other slot should be left
empty.
Aside from the FMC and its carrier, the following hardware/software components
are required:
* For the PCI version: a standard PC with at least one free 4x (or wider)
PCI-Express slot.
* For the VME version: a VME64x crate with a MEN A20/A25 CPU.
* 50-ohm cables with 1-pin LEMO 00 plugs for connecting the I/O signals.
* Any Linux distribution (validated with
CentOS 7 3.10.0-957.1.3.rt56.913.el7.x86_64)
Modes of Operation
==================
*fine-delay* can work in one or more of the following modes:
Pulse Delay
It produces one or more pulse(s) on selected outputs
a given time after an input trigger pulse.
Pulse Generator
Itproduces one or more pulse(s) on selected outputs
starting at an absolute time value programmed by the user.
In this mode, time base is usually provided by the White Rabbit network.
Time to Digital Converter
It tags all trigger pulses and delivers the timestamps to the user's
application.
.. image:: drawings/func.png
:alt: *fine-delay* operating modes.
Modes (pulse delay/generator) can be selected independently for each output.
For example, one can configure the output 1 to delay trigger pulses
by 1 us, and the output 2 to produce a pulse at the beginning of each second.
The TDC mode can be enabled for the input at any time and
does not interfere with the operation of the channels being time tagged.
Mechanical/Environmental
========================
.. image:: drawings/front_panels.png
:alt: *fine-delay* front panel connector layout.
**Mechanical and environmental specs:**
* Format: FMC (VITA 57), with rear zone for conduction cooling.
* Operating temperature range: 0 - 90 degC.
* Carrier connection: 160-pin Low Pin Count FMC connector.
Electrical
==========
Inputs/Outputs
--------------
* 1 trigger input (LEMO 00).
* 4 pulse outputs (LEMO 00).
* 2 LEDs (termination status and trigger indicator).
* Carrier communication via 160-pin Low Pin Count FMC connector.
Trigger input
-------------
* TTL/LVTTL levels, DC-coupled. Reception of a trigger pulse is indicated by
blinking the "TRIG" LED in the front panel.
* 2 kOhm or 50 Ohm input impedance (programmable via software).
50 Ohm termination is indicated by the "TERM" LED in the front panel.
* Power-up input impedance: 2 kOhm.
* Protected against short circuit, overcurrent (> 200 mA) and overvoltage
(up to +28 V).
* Maximum input pulse edge rise time: 20 ns.
Outputs
-------
* TTL-compatible levels DC-coupled: Voh = 3 V, Vol = 200 mV (50 Ohm load),
Voh = 6 V, Vol = 400 mV (high impedance).
* Output impedance: 50 Ohm (source-terminated).
* Rise/fall time: 2.5 ns (10%% - 90%%, 50 Ohm load).
* Power-up state: LOW (2 kOhm pulldown), guaranteed glitch-free.
* Protected against continuous short circuit, overcurrent and overvoltage
(up to +28 V).
Power supply
------------
* Used power supplies: P12V0, P3V3, P3V3_AUX, VADJ (voltage monitor only).
* Typical current consumption: 200 mA (P12V0) + 1.5 A (P3V3).
* Power dissipation: 7 W. Forced cooling is required.
Timing
======
.. image:: drawings/io_timing.png
:alt: *fine-delay* timing parameter definitions.
Time base
---------
* On-board oscillator accuracy: +/- 2.5 ppm (i.e. max. 2.5 ns error for a
delay of 1 ms).
* When using White Rabbit as the timing reference: depending on the
characteristics of the grandmaster clock and the carrier used. On SPEC
v 4.0 FMC carrier, the accuracy is better than 1 ns.
Input timing
------------
* Minimum pulse width: :math:`t_{IW}` = 50 ns. Pulses below 24 ns are rejected.
* Minimum gap between the last delayed output pulse and subsequent trigger
pulse: :math:`T_{LT}` = 50 ns.
* Input TDC performance: 400 ps pp accuracy, 27 ps resolution,
70 ps trigger-to-trigger rms jitter (measured at 500 kHz pulse rate).
Output timing
-------------
* Resolution: 10 ps.
* Accuracy (pulse generator mode): 300 ps.
* Train generation: trains of 1-65536 pulses or continuous square wave up
to 10 MHz.
* Output-to-output jitter (outputs programmed to the same delay): 10 ps rms.
* Output-to-output jitter (outputs programmed to to different delays, worst
case): 30 ps rms.
* Output pulse spacing (:math:`T_{SP}`) : 100 ns - 16 s. Adjustable in 10 ps
steps when both :math:`T_{PW}`, :math:`T_{GAP}` > 200 ns. Outside that range,
:math:`T_{SP}` resolution is limited to 4 ns.
* Output pulse start (:math:`t_{START}`) resolution: 10 ps for the rising edge
of the pulse, 10 ps for subsequent pulses if the condition above is met,
otherwise 4 ns.
Delay mode specific parameters
------------------------------
* Delay accuracy: < 1 ns.
* Trigger-to-output jitter: 80 ps rms.
* Trigger-to-output delay: minimum :math:`T_{DLY}` = 600 ns,
maximum :math:`T_{DLY}` = 120 s.
* Maximum trigger pulse rate: :math:`T_{DLY} + N*(T_{SP} + T_{GAP}) +` 100 ns,
where N = number of output pulses.
* Trigger pulses are ignored until the output with the biggest delay has
finished generation of the pulse(s).
Principles of Operation
=======================
.. note::
if you are an electronics engineer, you can skip this section, as
you will most likely find it rather boring.
.. image:: drawings/analog_digital_delays.png
:alt: Principle of operation of analog and digital delay generators.
Contrary to typical analog delay cards, which work by comparing an analog ramp
triggered by the input pulse with a voltage proportional to the desired delay,
*fine-delay* is a digital delay generator, which relies on time tag arithmetic.
The principle of operation of both generators is illustrated in the figure
above.
When a trigger pulse comes to the input, *fine-delay* first produces its'
precise time tag using a Time-to-Digital converter (TDC). Afterwards,
the time tag is summed together with the delay preset and the result is
passed to a digital pulse generator.
In its simplest form, it consists of a free running counter and a comparator.
When the counter reaches the value provided on the input, a pulse is produced
on the output.
Note that in order for the system to work correctly, both the TDC and
the Pulse Generator must use exactly the same time base (not shown on
the drawings).
Digital architecture brings several advantages compared to analog
predecessors: Timestamps generated by the TDC can be also passed to
the host system, and the Pulse Generators can be programmed with arbitrary
pulse start times instead of :math:`t_{TRIG} + T_{DLY}`. Therefore,
*fine-delay* can be used simultaneously as a TDC, pulse generator or
a pulse delay.
======================
Using the Provided API
======================
This chapter describes the higher level interface to the board,
designed for user applications to use. The code lives in the *lib}
subdirectory of this package. The directory uses a plain Makefile (not
a Kbuild one) so it can be copied elsewhere and compiled stand-alone.
Only, it needs a copy of ``fine-delay.h`` (which it currently pulls
from the parent directory) and the *zio* headers, retrieved using the
``ZIO`` environment variable).
.. _lib_init:
Initialization and Cleanup
==========================
Before using this library it must be initilized by calling
:c:func:`fdelay_init`. When you are not going to use the library anymore
call :c:func:`fdelay_exit` to release all allocated resources.
.. doxygenfunction:: fdelay_init
.. doxygenfunction:: fdelay_exit
In order to be able to handle a *fine-delay* device you must open it
with one of the following functions :c:func:`fdelay_open` or
:c:func:`fdelay_open_by_lun`. All these functions return a device token
which is required by most *fine-delay* functions. When you do not want to
use anymore the device, you should close it with :c:func:`fdelay_close`.
.. doxygenfunction:: fdelay_open
.. doxygenfunction:: fdelay_open_by_lun
.. doxygenfunction:: fdelay_close
Example code: all tools in ``tools/`` subdirectory.
.. _lib_time:
Time Management
===============
These are the primitives the library offers for time management, including
support for White Rabbit network synchronization.
.. doxygenfunction:: fdelay_set_time
.. doxygenfunction:: fdelay_get_time
.. doxygenfunction:: fdelay_set_host_time
.. doxygenfunction:: fdelay_wr_mode
.. doxygenfunction:: fdelay_check_wr_mode
Example code: ``fmc-fdelay-board-time`` tool.
.. _lib_input:
Input Configuration
===================
To configure the input channel for a board, the library offers the
following function and macros:
.. doxygenfunction:: fdelay_set_config_tdc
.. doxygenfunction:: fdelay_get_config_tdc
.. doxygendefine:: FD_TDCF_DISABLE_INPUT
.. doxygendefine:: FD_TDCF_DISABLE_TSTAMP
.. doxygendefine:: FD_TDCF_TERM_50
Example code: ``fmc-fdelay-term`` tool.
Reading Input Timestamps
========================
The library offers the following functions that deal with the input stamps:
.. doxygenfunction:: fdelay_read
.. doxygenfunction:: fdelay_fread
.. doxygenfunction:: fdelay_fileno_tdc
.. _lib_output:
Output Configuration
====================
The library offers the following functions for output configuration:
.. doxygenfunction:: fdelay_config_pulse
.. doxygenfunction:: fdelay_config_pulse_ps
.. doxygenfunction:: fdelay_get_config_pulse
.. doxygenfunction:: fdelay_get_config_pulse_ps
.. doxygenfunction:: fdelay_has_triggered
The configuration functions receive a time configuration. The
starting time is passed as ``struct fdelay_time``, while the
pulse end and loop period are passed using either the same structure
or a scalar number of picoseconds. These are the relevant structures:
.. doxygenstruct:: fdelay_time
:members:
.. doxygenstruct:: fdelay_pulse
:members:
.. doxygenstruct:: fdelay_pulse_ps
:members:
Example code: ``fmc-fdelay-pulse`` tool.
Miscellanous functions
======================
.. doxygenfunction:: fdelay_pico_to_time
.. doxygenfunction:: fdelay_time_to_pico
.. doxygenfunction:: fdelay_read_temperature
==================
Command Line Tools
==================
This chapter describes the command line tools that come with the
driver and reside in the ``tools/`` subdirectory. They are provided
as diagnostic utilities and to demonstrate how to use the library.
General Command Line Conventions
================================
Most tools accept the following command-line options, in a
consistent way:
``-d <devid>``
Used to select one board among several. See the description
of *fdelay_open* in :ref:`Initialization and Cleanup<lib_init>`.
fmc-fdelay-list
===============
The command takes no arguments. It reports the list of available
boards in the current system:::
spusa# ./tools/fmc-fdelay-list
Fine-Delay Device ID 0005
Fine-Delay Device ID 0004
fmc-fdelay-term
===============
The command can be used to activate or deactivate the 50 ohm
termination resistor.
In addition to the ``-d`` argument the command receives one optional
argument, either ``1`` or ``on`` (activate termination)
or ``0`` or ``off`` (deactivate termination).
::
spusa# ./tools/fmc-fdelay-term -d 0x5 on
./tools/fmc-fdelay-term: termination is on
If no optional argument is passed the termination status is reported back but
not changed.
fmc-fdelay-board-time
=====================
The command is used to act on the time notion of the *fine-delay* card.
In addition to the ``-d`` argument the command receives one mandatory
argument, that is either a command or a floating point number.
The number is the time, in seconds, to be set in the card only if running
with the local oscillator; the command is one of the following ones:
get
Read board time and print to *stdout*.
host
Set board time from host time
wr
Lock the boards to White Rabbit time. It may block if no White
Rabbit is there. No timeout is currently available.
local
Detach the board from White Rabbit, and run local time instead.
Examples:::
spusa# ./tools/fmc-fdelay-board-time -d 0x5 25.5
spusa# ./tools/fmc-fdelay-board-time -d 0x5 get
25.504007360
spusa# ./tools/fmc-fdelay-board-time -d 0x5 get
34.111048968
spusa# ./tools/fmc-fdelay-board-time -d 0x5 host
spusa# ./tools/fmc-fdelay-board-time -d 0x5 get
1335974946.493415600
fmc-fdelay-input
================
The tool reports input pulses to stdout. It receives the
usual ``-d`` argument to select one board.
It receives the following options:
``-c <count>``
Number of pulses to print. Default (0) means run forever.
``-n``
Nonblocking mode: just print what is pending in the buffer.
``-f``
Floating point: print as a floatingpoint seconds.pico value.
The default is a human-readable string, where the decimal part
is split.
``-r``
Raw output: print the three hardware timestamps, in decimal.
This an example output, reading a pps signal through a 16ns cable:
::
spusa.root# ./tools/fmc-fdelay-input -d 0x5 -c 3
seq 10921: time 11984:000,000,015,328 ps
seq 10922: time 11985:000,000,015,410 ps
seq 10923: time 11986:000,000,015,248 ps
spusa.root# ./tools/fmc-fdelay-input -d 0x5 -c 3 -r
seq 10924: raw utc 11987, coarse 1, frac 3773
seq 10925: raw utc 11988, coarse 1, frac 3814
seq 10926: raw utc 11989, coarse 1, frac 3794
spusa.root# ./tools/fmc-fdelay-input -d 0x5 -c 3 -f
seq 10927: time 11990.000000015328
seq 10928: time 11991.000000015410
seq 10929: time 11992.000000015410
In a future release we'll support reading concurrently from several
boards.
fmc-fdelay-pulse
================
The program can be used to program one of the output channels to
output a sequence of pulses. It can parse the following command-line
options:
``-o <output>``
Output channels are numbered 1 to 4, as written on the device panel.
Each command invocation can set only one output channel; the
last ``-o`` specified takes precedence.
``-c <count>``
Output repeat count: 0 is the default and means forever
``-m <mode>``
Output mode. Can be ``pulse``, ``delay`` or ``disable``.
``-r <reltime>``
Output pulse at a relative time in the future. The time is
a fraction of a second, specified as for ``-T`` and ``-w``,
described below. For delay mode the time is used as
a delay value from input events; for pulse mode the time
represents a fraction of the next absolute second.
``-D <date>``
Output pulse at a specified date. The argument is parsed
as ``<seconds>:<nanoseconds>``.
``-T <period>``, ``-w <width>``
Period and width of the output signal. A trailing ``m``,
``u``, ``n``, ``p`` means milli, micro, nano, pico, resp.
The parser supports additions and subtractions, e.g.
``50m-20n``.
The period defaults to 100ms and the width defaults to 8us
``-t``
Wait for the trigger to happen before returning. The boards reports
a trigger event when the requested pulse sequence is initiated,
either because the absolute time arrived or because an input
pulse was detected and the requested delay elapsed.
``-p``, ``-1``
Pulse-per-seconds and 10MHz. These are shorthands setting many
parameters.
``-v``
Verbose: report action to stdout before telling the driver.
This is, for example, how verbose operation reports the request for a single
pulse 300ns wide, 2 microseconds into the next second.:::
spusa.root# ./tools/fmc-fdelay-board-time -d 0x5 get; \
./tools/fmc-fdelay-pulse -d 0x5 -o 1 -m pulse -r 2u -w 300n -c 1 -t
WR Status: disabled.
Time: 13728.801090400
Channel 1: pulse generator mode
start at: 13729:000,002,000,000 ps
pulse width: 0:000,000,300,000 ps
period: 0:100,000,000,000 ps
fmc-fdelay-status
=================
The program reports the current output status of the four channels,
both in human-readable and raw format. The receives no arguments
besides the usual ``-d``.::
spusa.root# ./tools/fmc-fdelay-status -d 0x5
Channel 1: pulse generator mode (triggered)
start at: 13729:000,002,000,000 ps
pulse width: 0:000,000,300,000 ps
period: 0:100,000,000,000 ps
Channel 2: disabled
Channel 3: disabled
Channel 4: disabled
Please note that the tool reads back hardware values, which are already
fixed for calibration delays. A difference in value may depends on the
``delay-offset`` value for the channel, according to calibration.
===============
Troubleshooting
===============
This chapters lists a few errors that may happen and how to deal with
them.
make modules_install misbehaves
===============================
The command ``sudo make modules_install`` may place the modules in the wrong
directory or fail with an error like:::
make: \*\*\* /lib/modules/3.10/build: No such file or directory.
This happens when you compiled by setting ``LINUX=`` and your
*sudo* is not propagating the environment to its child processes.
In this case, you should run this command instead::
sudo make modules_install LINUX=$LINUX
Version Mismatch
================
The *fdelay* library may report a version mismatch like this:::
spusa# ./tools/fmc-fdelay-board-time -d 0x5 get
Incompatible version driver-library
This reports a difference in the way ZIO attributes are laid out, so user
space may exchange wrong data in the ZIO control block, or may try to
access inexistent files in */sys*. I suggest recompiling both the kernel
driver and user space from a single release of the source package.
.tmp_versions
.*.o.d
KBUILD_EXTRA_SYMBOLS += $(ZIO_EXTRA_SYMBOLS-y)
KBUILD_EXTRA_SYMBOLS += $(FMC_EXTRA_SYMBOLS-y)
ccflags-y += -DVERSION=\"$(VERSION)\"
ccflags-y += -I$(src)
ccflags-y += -I$(ZIO_ABS)/include
ccflags-y += -I$(FMC_ABS)/include
# Extract ZIO minimum compatible version
ccflags-y += -D__ZIO_MIN_MAJOR_VERSION=$(shell echo $(ZIO_VERSION) | cut -d '-' -f 1 | cut -d '.' -f 1 | tr -d 'v'; )
ccflags-y += -D__ZIO_MIN_MINOR_VERSION=$(shell echo $(ZIO_VERSION) | cut -d '-' -f 1 | cut -d '.' -f 2; )
# add versions of supermodule. It is useful when fine-delay-sw is included as sub-module
# of a bigger project that we want to track
ifdef CONFIG_SUPER_REPO
ifdef CONFIG_SUPER_REPO_VERSION
SUBMODULE_VERSIONS += MODULE_INFO(version_$(CONFIG_SUPER_REPO),\"$(CONFIG_SUPER_REPO_VERSION)\");
endif
endif
# add versions of used submodules
SUBMODULE_VERSIONS += MODULE_INFO(version_zio,\"$(ZIO_VERSION)\");
ccflags-y += -DADDITIONAL_VERSIONS="$(SUBMODULE_VERSIONS)"
subdirs-ccflags-y = $(ccflags-y)
obj-m := fmc-fine-delay.o
obj-m += fmc-fine-delay-spec.o
obj-m += fmc-fine-delay-svec.o
fmc-fine-delay-objs = fd-zio.o fd-irq.o fd-core.o
fmc-fine-delay-objs += onewire.o spi.o gpio.o
fmc-fine-delay-objs += acam.o calibrate.o pll.o time.o
fmc-fine-delay-objs += calibration.o
fmc-fine-delay-spec-objs := fmc-fine-delay-spec-core.o
fmc-fine-delay-svec-objs := fmc-fine-delay-svec-core.o
\ No newline at end of file
# SPDX-License-Identifier: GPL-2.0-or-later
#
# Copyright (C) 2019 CERN
-include Makefile.specific
# include parent_common.mk for buildsystem's defines
#use absolute path for REPO_PARENT
REPO_PARENT ?= $(shell /bin/pwd)/../..
-include $(REPO_PARENT)/parent_common.mk
CPPCHECK ?= cppcheck
DKMS ?= 0
CURDIR := $(shell /bin/pwd)
KVERSION ?= $(shell uname -r)
LINUX ?= /lib/modules/$(KVERSION)/build
ifdef REPO_PARENT
ZIO ?= $(REPO_PARENT)/fmc/zio
FMC ?= $(REPO_PARENT)/fmc-sw
endif
ifeq ($(DKMS), 1)
# Take last installed version (if installed using RPM it should be OK)
ZIO_VERSION ?= $(shell basename $(shell ls -d $(DKMSTREE)/zio/* | grep -E "\/[0-9]+\.[0-9]+\.[0-9]+" | sort -V | tail -n 1))
ZIO_ABS ?= $(DKMSTREE)/zio/$(ZIO_VERSION)/source
ZIO_EXTRA_SYMBOLS-y = $(DKMSTREE)/zio/kernel-$(KVERSION)-$(shell uname -p)/module/Module.symvers
else
ifndef ZIO
$(error "Missing ZIO environment variable")
endif
ifndef FMC
$(error "Missing FMC environment variable")
endif
ZIO_ABS ?= $(abspath $(ZIO))
ZIO_EXTRA_SYMBOLS-y = $(ZIO_ABS)/drivers/zio/Module.symvers
ZIO_VERSION ?= $(shell cd $(ZIO_ABS); git describe --always --dirty --long --tags)
FMC_ABS ?= $(abspath $(FMC))
FMC_EXTRA_SYMBOLS-y = $(FMC_ABS)/drivers/fmc/Module.symvers
endif
GIT_VERSION = $(shell git describe --always --dirty --long --tags)
all modules:
$(MAKE) -C $(LINUX) M=$(CURDIR) ZIO_ABS=$(ZIO_ABS) FMC_ABS=$(FMC_ABS) \
ZIO_EXTRA_SYMBOLS-y=$(ZIO_EXTRA_SYMBOLS-y) \
FMC_EXTRA_SYMBOLS-y=$(FMC_EXTRA_SYMBOLS-y) \
ZIO_VERSION=$(ZIO_VERSION) \
GIT_VERSION=$(GIT_VERSION) \
modules
install modules_install: modules
$(MAKE) -C $(LINUX) M=$(CURDIR) modules_install
# be able to run the "clean" rule even if $(LINUX) is not valid
clean:
rm -rf *.o *~ .*.cmd *.ko *.mod.c .tmp_versions Module.symvers \
Module.markers modules.order
cppcheck:
$(CPPCHECK) -q -I. -I$(ZIO_ABS)/include -I$(FMC_BUS_ABS)/ --enable=all *.c *.h
/*
* Accessing the ACAM chip and configuring it.
*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2 as published by the Free Software Foundation or, at your
* option, any later version.
*/
#include <linux/jiffies.h>
#include <linux/io.h>
#include <linux/delay.h>
//#include <linux/math64.h>
#include <linux/moduleparam.h>
#include "fine-delay.h"
#include "hw/fd_main_regs.h"
#include "hw/acam_gpx.h"
int fd_calib_period_s = 5;
module_param_named(calib_s, fd_calib_period_s, int, 0444);
/*
* Calculation is fixed point: picoseconds and 16 decimals (i.e. ps << 16).
* We know the bin is small, but the Tref is several nanos so we need 64 bits
* (although our current values fit in 32 bits after the division)
*/
#define ACAM_FP_BIN ((int)(ACAM_DESIRED_BIN * (1 << 16)))
#define ACAM_FP_TREF (((1000LL * 1000 * 1000) << 16) / ACAM_CLOCK_FREQ_KHZ)
/* Default values of control registers for the ACAM TDC working in G-Mode
(eeprom values are obsolete) */
#define ACAM_GMODE_START_OFFSET 10000
#define ACAM_GMODE_ASOR 17000
#define ACAM_GMODE_ATMCR (26 | (1500 << 8))
#define ACAM_GMODE_ADSFR 84977
static int acam_calc_pll(uint64_t tref, int bin, int *hsdiv_out,
int *refdiv_out)
{
uint64_t tmpll;
int x, refdiv, hsdiv;
int32_t rem;
/*
* Tbin(I-mode) = (Tref << refdiv) / (216 * hsdiv)
*
* so, calling X the value "hsdiv >> refdiv" we have
*
* X = Tref / (216 * Tbin)
*
* Then, we can choose refdiv == 7 to have the best bits,
* and then shift out the zeros to get smaller values.
*
*/
if (0) {
x = (tref << 16) / 216 / bin;
//printf("x = %lf\n", (double)x / (1<<16));
} else {
/* We can't divide 64 bits in kernel space */
tmpll = div_u64_rem(tref << 16, 216, &rem);
x = div_u64_rem(tmpll, bin, &rem);
}
/* Now, shift out the max bits (usually 7) and drop decimal part */
refdiv = ACAM_MAX_REFDIV;
hsdiv = (x << refdiv) >> 16;
/* Check the first decimal bit and approximate */
if ((x << refdiv) & (1 << 15))
hsdiv++;
/* until we have zeroes as LSB, shift out to decrease pll quotient */
while (refdiv > 0 && !(hsdiv & 1)) {
refdiv--;
hsdiv >>= 1;
}
*hsdiv_out = hsdiv;
*refdiv_out = refdiv;
/* Finally, calculate what we really have */
if (0) {
bin = (tref << refdiv) / 216 / hsdiv;
} else {
tmpll = div_u64_rem(tref << refdiv, 216, &rem);
bin = div_u64_rem(tmpll, hsdiv, &rem);
}
return (bin + 1); /* We always return the bin size in the I mode. Other modes should scale it appropriately. */
}
static void acam_set_address(struct fd_dev *fd, int addr)
{
if (addr == fd->acam_addr)
return;
if (fd->acam_addr == -1) {
/* first time */
fd_gpio_dir(fd, 0xf00, FD_GPIO_OUT);
}
fd_gpio_val(fd, 0xf00, addr << 8);
fd->acam_addr = addr;
}
/* Warning: acam_readl and acam_writel only work if GCR.BYPASS is set */
uint32_t acam_readl(struct fd_dev *fd, int reg)
{
acam_set_address(fd, reg);
fd_writel(fd, FD_TDCSR_READ, FD_REG_TDCSR);
return fd_readl(fd, FD_REG_TDR) & ACAM_MASK;
}
void acam_writel(struct fd_dev *fd, int val, int reg)
{
acam_set_address(fd, reg);
fd_writel(fd, val, FD_REG_TDR);
fd_writel(fd, FD_TDCSR_WRITE, FD_REG_TDCSR);
}
static void acam_set_bypass(struct fd_dev *fd, int on)
{
/* warning: this clears the "input enable" bit: call at init only */
fd_writel(fd, on ? FD_GCR_BYPASS : 0, FD_REG_GCR);
}
static inline int acam_is_pll_locked(struct fd_dev *fd)
{
return !(acam_readl(fd, 12) &AR12_NotLocked);
}
/* Two test functions to verify the bus is working -- Tom */
static int acam_test_addr_bit(struct fd_dev *fd, int base, int bit,
int data)
{
int addr1 = base;
int addr2 = base + (1<<bit);
int reg;
reg = acam_readl(fd, addr1) & ~data;
acam_writel(fd, reg, addr1); /* zero the data mask */
reg = acam_readl(fd, addr2) | data;
acam_writel(fd, reg, addr2); /* set the data mask */
if ((acam_readl(fd, addr1) & data) != 0)
goto out;
if ((acam_readl(fd, addr2) & data) != data)
goto out;
/* the other way around */
reg = acam_readl(fd, addr2) & ~data;
acam_writel(fd, reg, addr2); /* zero the data mask */
reg = acam_readl(fd, addr1) | data;
acam_writel(fd, reg, addr1); /* set the data mask */
if ((acam_readl(fd, addr2) & data) != 0)
goto out;
if ((acam_readl(fd, addr1) & data) != data)
goto out;
return 0;
out:
pr_err("%s: ACAM address bit %i failure\n", KBUILD_MODNAME, bit);
return -EIO;
}
static int acam_test_bus(struct fd_dev *fd)
{
int err = 0, i, v;
/* Use register 5 to checke the data bits */
for(i = 0; i & ACAM_MASK; i <<= 1) {
acam_writel(fd, i, 5);
acam_readl(fd, 0);
v = acam_readl(fd, 5);
if (v != i)
goto out;
acam_writel(fd, ~i & ACAM_MASK, 5);
acam_readl(fd, 0);
v = acam_readl(fd, 5);
if (v != (~i & ACAM_MASK))
goto out;
}
err += acam_test_addr_bit(fd, 0, 0, 0x000001);
err += acam_test_addr_bit(fd, 1, 1, 0x000008);
err += acam_test_addr_bit(fd, 0, 2, 0x000001);
err += acam_test_addr_bit(fd, 3, 3, 0x010000);
if (err)
return -EIO;
return 0;
out:
pr_err("%s: ACAM data bit 0x%06x failure\n", KBUILD_MODNAME, i);
return -EIO;
}
/* We need to write come static configuration in the registers */
struct acam_init_data {
int addr;
int val;
};
/* Commented values are not constant, they are added at runtime (see later) */
static struct acam_init_data acam_init_rmode[] = {
{0, AR0_ROsc | AR0_RiseEn0 | AR0_RiseEn1 | AR0_HQSel},
{1, AR1_Adj(0, 0) | AR1_Adj(1, 2) | AR1_Adj(2, 6) |
AR1_Adj(3, 0) | AR1_Adj(4, 2) | AR1_Adj(5, 6) | AR1_Adj(6, 0)},
{2, AR2_RMode | AR2_Adj(7, 2) | AR2_Adj(8, 6)},
{3, 0},
{4, AR4_EFlagHiZN},
{5, AR5_StartRetrig
| 0 /* AR5_StartOff1(hw->calib.acam_start_offset) */
| AR5_MasterAluTrig},
{6, AR6_Fill(200) | AR6_PowerOnECL},
{7, /* AR7_HSDiv(hsdiv) | AR7_RefClkDiv(refdiv) */ 0
| AR7_ResAdj | AR7_NegPhase},
{11, 0x7ff0000},
{12, 0x0000000},
{14, 0},
/* finally, reset */
{4, AR4_EFlagHiZN | AR4_MasterReset | AR4_StartTimer(0)},
};
/* Commented values are not constant, they are added at runtime (see later) */
static struct acam_init_data acam_init_gmode[] = {
{0, AR0_ROsc | AR0_RiseEn0 | AR0_RiseEn1 | AR0_HQSel},
{1, AR1_Adj(0, 0) | AR1_Adj(1, 0) | AR1_Adj(2, 5) |
AR1_Adj(3, 0) | AR1_Adj(4, 5) | AR1_Adj(5, 0) | AR1_Adj(6, 5)},
{2, AR2_GMode | AR2_Adj(7, 0) | AR2_Adj(8, 5) |
AR2_DelRise1(0) | AR2_DelFall1(0) | AR2_DelRise2(0) | AR2_DelFall2(0)},
{3, AR3_DelTx(1,3) | AR3_DelTx(2,3) | AR3_DelTx(3,3) | AR3_DelTx(4,3) |
AR3_DelTx(5,3) | AR3_DelTx(6,3) | AR3_DelTx(7,3) | AR3_DelTx(8,3) |
AR3_RaSpeed(0,3) | AR3_RaSpeed(1,3) | AR3_RaSpeed(2,3)},
{4, AR4_EFlagHiZN | AR4_RaSpeed(3,3) | AR4_RaSpeed(4,3) |
AR4_RaSpeed(5,3) | AR4_RaSpeed(6,3) | AR4_RaSpeed(7,3) | AR4_RaSpeed(8,3)},
{5, AR5_StartRetrig
| 0 /* AR5_StartOff1(hw->calib.acam_start_offset) */
| AR5_MasterAluTrig},
{6, AR6_Fill(200) | AR6_PowerOnECL},
{7, /* AR7_HSDiv(hsdiv) | AR7_RefClkDiv(refdiv) */ 0
| AR7_ResAdj | AR7_NegPhase},
{11, 0x7ff0000},
{12, 0x0000000},
{14, 0},
/* finally, reset */
{4, AR4_EFlagHiZN | AR4_MasterReset | AR4_StartTimer(0)},
};
static struct acam_init_data acam_init_imode[] = {
{0, AR0_TRiseEn(0) | AR0_HQSel | AR0_ROsc},
{2, AR2_IMode},
{5, AR5_StartOff1(3000) | AR5_MasterAluTrig},
{6, 0},
{7, /* AR7_HSDiv(hsdiv) | AR7_RefClkDiv(refdiv) */ 0
| AR7_ResAdj | AR7_NegPhase},
{11, 0x7ff0000},
{12, 0x0000000},
{14, 0},
/* finally, reset */
{4, AR4_EFlagHiZN | AR4_MasterReset | AR4_StartTimer(0)},
};
struct acam_mode_setup {
enum fd_acam_modes mode;
char *name;
struct acam_init_data *data;
int data_size;
};
static struct acam_mode_setup fd_acam_table[] = {
{
ACAM_RMODE, "R",
acam_init_rmode, ARRAY_SIZE(acam_init_rmode),
},
{
ACAM_IMODE, "I",
acam_init_imode, ARRAY_SIZE(acam_init_imode)
},
{
ACAM_GMODE, "G",
acam_init_gmode, ARRAY_SIZE(acam_init_gmode)
},
};
/* To configure the thing, follow the table, but treat 5 and 7 as special */
static int __acam_config(struct fd_dev *fd, struct acam_mode_setup *s)
{
int i, hsdiv, refdiv, reg7val;
struct acam_init_data *p;
uint32_t regval;
unsigned long j;
fd->bin = acam_calc_pll(ACAM_FP_TREF, ACAM_FP_BIN, &hsdiv, &refdiv);
reg7val = AR7_HSDiv(hsdiv) | AR7_RefClkDiv(refdiv);
pr_debug("%s: config for %s-mode (bin 0x%x, hsdiv %i, refdiv %i)\n",
__func__, s->name, fd->bin, hsdiv, refdiv);
/* Disable TDC inputs prior to configuring */
fd_writel(fd, FD_TDCSR_STOP_DIS | FD_TDCSR_START_DIS, FD_REG_TDCSR);
/* Disable the ACAM PLL for a while to make sure it is reset */
acam_writel(fd, 0, 0);
acam_writel(fd, 7, 0);
msleep(100);
for (p = s->data, i = 0; i < s->data_size; p++, i++) {
regval = p->val;
if (p->addr == 7)
regval |= reg7val;
if (p->addr == 5 && s->mode == ACAM_RMODE) /* FIXME: gmode? */
regval |= AR5_StartOff1(ACAM_GMODE_START_OFFSET);
if (p->addr == 5 && s->mode == ACAM_GMODE)
regval |= AR5_StartOff1(ACAM_GMODE_START_OFFSET);
if (p->addr == 6 && s->mode == ACAM_GMODE)
regval |= AR6_StartOff2(ACAM_GMODE_START_OFFSET);
acam_writel(fd, regval, p->addr);
}
/* Wait for the oscillator to lock */
j = jiffies + 2 * HZ;
while (time_before(jiffies, j)) {
if (acam_is_pll_locked(fd))
break;
msleep(10);
}
if (time_after_eq(jiffies, j)) {
pr_err("%s: ACAM PLL does not lock\n", __func__);
return -EIO;
}
/* after config, set the FIFO address for further reads */
acam_set_address(fd, 8);
return 0;
}
int fd_acam_config(struct fd_dev *fd, enum fd_acam_modes mode)
{
struct acam_mode_setup *s;
int i;
for (s = fd_acam_table, i = 0; i < ARRAY_SIZE(fd_acam_table); s++, i++)
if (mode == s->mode)
return __acam_config(fd, s);
pr_err("%s: invalid mode %i\n", __func__, mode);
return -EINVAL;
}
int fd_acam_init(struct fd_dev *fd)
{
int ret;
fd->acam_addr = -1; /* First time must be activated */
acam_set_bypass(fd, 1); /* Driven by host, not core */
if ( (ret = acam_test_bus(fd)) )
return ret;
if ( (ret = fd_acam_config(fd, ACAM_IMODE)) )
return ret;
if ( (ret = fd_calibrate_outputs(fd)) )
return ret;
if ( (ret = fd_acam_config(fd, ACAM_GMODE)) )
return ret;
acam_set_bypass(fd, 0); /* Driven by core, not host */
/* Clear and disable the timestamp readout buffer */
fd_writel(fd, FD_TSBCR_PURGE | FD_TSBCR_RST_SEQ, FD_REG_TSBCR);
/*
* Program the ACAM-specific TS registers w pre-defined calib values:
* - bin -> internal timebase scalefactor (ADSFR),
* - Start offset (must be consistent with value in ACAM reg 4)
* - timestamp merging control register (ATMCR)
* GMode fix: we no longer use the values from the EEPROM (they are fixed anyway)
*/
fd_writel(fd, ACAM_GMODE_ADSFR, FD_REG_ADSFR);
fd_writel(fd, ACAM_GMODE_ASOR, FD_REG_ASOR);
fd_writel(fd, ACAM_GMODE_ATMCR, FD_REG_ATMCR);
fd->temp_ready = 0;
/* Prepare the timely recalibration */
setup_timer(&fd->temp_timer, fd_update_calibration, (unsigned long)fd);
if (fd_calib_period_s)
mod_timer(&fd->temp_timer, jiffies + HZ * fd_calib_period_s);
return 0;
}
void fd_acam_exit(struct fd_dev *fd)
{
del_timer_sync(&fd->temp_timer);
}
/*
* Calibrate the output path.
*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2 as published by the Free Software Foundation or, at your
* option, any later version.
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/delay.h>
//#include <linux/math64.h>
#include "fine-delay.h"
#include "hw/fd_main_regs.h"
#include "hw/acam_gpx.h"
#include "hw/fd_channel_regs.h"
/* This is the same as in ./acam.c: use only at init time */
static void acam_set_bypass(struct fd_dev *fd, int on)
{
fd_writel(fd, on ? FD_GCR_BYPASS : 0, FD_REG_GCR);
}
static int acam_test_delay_transfer_function(struct fd_dev *fd)
{
/* FIXME */
return 0;
}
/* Evaluates 2nd order polynomial. Coefs have 32 fractional bits. */
static int fd_eval_polynomial(struct fd_dev *fd)
{
int64_t x = fd->temp;
int64_t *coef = fd->calib.frr_poly;
return (coef[0] * x * x + coef[1] * x + coef[2]) >> 32;
}
/*
* Measures the the FPGA-generated TDC start and the output of one of
* the fine delay chips (channel) at a pre-defined number of taps
* (fine). Retuns the delay in picoseconds. The measurement is
* repeated and averaged (n_avgs) times. Also, the standard deviation
* of the result can be written to (sdev) if it's not NULL.
*/
struct delay_stats {
uint64_t avg;
uint64_t min;
uint64_t max;
};
/* Note: channel is the "internal" one: 0..3 */
static uint64_t output_delay_ps(struct fd_dev *fd, int ch, int fine, int n,
struct delay_stats *stats)
{
int i;
uint64_t *results;
uint64_t res, acc = 0;
int rem;
struct device *dev = &fd->pdev->dev;
results = kmalloc(n * sizeof(*results), GFP_KERNEL);
if (!results)
return -ENOMEM;
/* Disable the output for the channel being calibrated */
fd_gpio_clr(fd, FD_GPIO_OUTPUT_EN(FD_CH_EXT(ch)));
/* Enable the stop input in ACAM for the channel being calibrated */
acam_writel(fd, AR0_TRiseEn(0) | AR0_TRiseEn(FD_CH_EXT(ch))
| AR0_HQSel | AR0_ROsc, 0);
/* Program the output delay line setpoint */
fd_ch_writel(fd, ch, fine, FD_REG_FRR);
fd_ch_writel(fd, ch, FD_DCR_ENABLE | FD_DCR_MODE | FD_DCR_UPDATE,
FD_REG_DCR);
fd_ch_writel(fd, ch, FD_DCR_FORCE_DLY | FD_DCR_ENABLE, FD_REG_DCR);
/*
* Set the calibration pulse mask to genrate calibration
* pulses only on one channel at a time. This minimizes the
* crosstalk in the output buffer which can severely decrease
* the accuracy of calibration measurements
*/
fd_writel(fd, FD_CALR_PSEL_W(1 << ch), FD_REG_CALR);
udelay(1);
/* Do n_avgs single measurements and average */
for (i = 0; i < n; i++) {
uint32_t fr;
/* Re-arm the ACAM (it's working in a single-shot mode) */
fd_writel(fd, FD_TDCSR_ALUTRIG, FD_REG_TDCSR);
udelay(1);
/* Produce a calib pulse on the TDC start and the output ch */
fd_writel(fd, FD_CALR_CAL_PULSE |
FD_CALR_PSEL_W(1 << ch), FD_REG_CALR);
udelay(1);
/* read the tag, convert to picoseconds (fixed point: 16.16) */
fr = acam_readl(fd, 8 /* fifo */) & 0x1ffff;
res = fr * fd->bin;
if (fd->verbose > 3)
dev_info(dev, "%s: ch %i, fine %i, bin %x got %08x, "
"res 0x%016llx\n", __func__, ch, fine,
fd->bin, fr, res);
results[i] = res;
acc += res;
}
fd_ch_writel(fd, ch, 0, FD_REG_DCR);
/* Calculate avg, min max */
acc = div_u64_rem((acc + n / 2), n, &rem);
if (stats) {
stats->avg = acc;
stats->min = ~0LL;
stats->max = 0LL;
for (i = 0; i < n; i++) {
if (results[i] > stats->max) stats->max = results[i];
if (results[i] < stats->min) stats->min = results[i];
}
if (fd->verbose > 2)
dev_info(dev, "%s: ch %i, taps %i, count %i, result %llx "
"(max-min %llx)\n", __func__, ch, fine, n,
stats->avg, stats->max - stats->min);
}
kfree(results);
return acc;
}
static void __pr_fixed(char *head, uint64_t val, char *tail)
{
printk("%s%i.%03i%s", head, (int)(val >> 16),
((int)(val & 0xffff) * 1000) >> 16, tail);
}
static int fd_find_8ns_tap(struct fd_dev *fd, int ch)
{
int l = 0, mid, r = FD_NUM_TAPS - 1;
uint64_t bias, dly;
struct delay_stats stats;
struct device *dev = &fd->pdev->dev;
/*
* Measure the delay at zero setting, so it can be further
* subtracted to get only the delay part introduced by the
* delay line (ingoring the TDC, FPGA and routing delays).
* Use a binary search of the delay value.
*/
bias = output_delay_ps(fd, ch, 0, FD_CAL_STEPS, NULL);
while( r - l > 1) {
mid = ( l + r) / 2;
dly = output_delay_ps(fd, ch, mid, FD_CAL_STEPS, &stats) - bias;
if (fd->verbose > 1) {
dev_info(dev, "%s: ch%i @ %-5i: ", __func__, ch, mid);
__pr_fixed("bias ", bias, ", ");
__pr_fixed("min ", stats.min - bias, ", ");
__pr_fixed("avg ", stats.avg - bias, ", ");
__pr_fixed("max ", stats.max - bias, "\n");
}
if(dly < 8000 << 16)
l = mid;
else
r = mid;
}
return l;
}
/**
* fd_calibrate_outputs
* It calibrates the delay line by finding the correct 8ns-tap value
* for each channel. This is done during ACAM initialization, so on driver
* probe.
*/
int fd_calibrate_outputs(struct fd_dev *fd)
{
int ret, ch;
int measured, fitted, new;
acam_set_bypass(fd, 1); /* not useful */
fd_writel(fd, FD_TDCSR_START_EN | FD_TDCSR_STOP_EN, FD_REG_TDCSR);
if ((ret = acam_test_delay_transfer_function(fd)) < 0)
return ret;
fd_read_temp(fd, 0);
fitted = fd_eval_polynomial(fd);
for (ch = FD_CH_1; ch <= FD_CH_LAST; ch++) {
measured = fd_find_8ns_tap(fd, ch);
new = measured;
fd->ch[ch].frr_offset = new - fitted;
fd_ch_writel(fd, ch, new, FD_REG_FRR);
fd->ch[ch].frr_cur = new;
if (fd->verbose > 1) {
dev_info(&fd->pdev->dev,
"%s: ch%i: 8ns @%i (f %i, off %i, t %i.%02i)\n",
__func__, FD_CH_EXT(ch),
new, fitted, fd->ch[ch].frr_offset,
fd->temp / 16, (fd->temp & 0xf) * 100 / 16);
}
}
return 0;
}
/**
* fd_update_calibration
* Called from a timer any few seconds. It updates the Delay line tap
* according to the measured temperature
*/
void fd_update_calibration(unsigned long arg)
{
struct fd_dev *fd = (void *)arg;
int ch, fitted, new;
fd_read_temp(fd, 0 /* not verbose */);
fitted = fd_eval_polynomial(fd);
for (ch = FD_CH_1; ch <= FD_CH_LAST; ch++) {
new = fitted + fd->ch[ch].frr_offset;
fd_ch_writel(fd, ch, new, FD_REG_FRR);
fd->ch[ch].frr_cur = new;
dev_dbg(&fd->pdev->dev,
"%s: ch%i: 8ns @%i (f %i, off %i, t %i.%02i)\n",
__func__, FD_CH_EXT(ch),
new, fitted, fd->ch[ch].frr_offset,
fd->temp / 16, (fd->temp & 0xf) * 100 / 16);
}
mod_timer(&fd->temp_timer, jiffies + HZ * fd_calib_period_s);
}
/*
* Code related to on-eeprom calibration: retrieving, defaulting, updating.
*
* Copyright (C) 2013 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2 as published by the Free Software Foundation or, at your
* option, any later version.
*/
#include <linux/time.h>
#include <linux/jhash.h>
#include <linux/stat.h>
#include <linux/slab.h>
#include <linux/zio.h>
#include "fine-delay.h"
static struct fd_calibration fd_calib_default = {
.magic = FD_MAGIC_FPGA,
.version = 3,
.date = 0x20130427,
.frr_poly = { -165202LL, -29825595LL, 3801939743082LL },
.zero_offset = { -38186, -38155, -38147, -38362 },
.tdc_zero_offset = 127500,
.vcxo_default_tune = 41711,
};
static off_t fd_calib_find_offset(const void *data, size_t len)
{
int i;
for (i = 0; i < len; i += 4) {
uint32_t sign = be32_to_cpup((const uint32_t *)(data + i));
if (sign == FD_MAGIC_FPGA)
return i;
}
return -ENODATA;
}
/**
* @calib: calibration data
*
* We know for sure that our structure is only made of 16bit fields
*/
static void fd_calib_endianess_to_cpus(struct fd_calibration *calib)
{
int i;
calib->magic = be32_to_cpu(calib->magic);
calib->size = be16_to_cpu(calib->size);
calib->version = be16_to_cpu(calib->version);
calib->date = be32_to_cpu(calib->date);
for (i = 0; i < ARRAY_SIZE(calib->frr_poly); i++)
calib->frr_poly[i] = be64_to_cpu(calib->frr_poly[i]);
for (i = 0; i < ARRAY_SIZE(calib->zero_offset); i++)
calib->zero_offset[i] = be32_to_cpu(calib->zero_offset[i]);
calib->tdc_zero_offset = be32_to_cpu(calib->tdc_zero_offset);
calib->vcxo_default_tune = be32_to_cpu(calib->vcxo_default_tune);
}
/**
* @calib: calibration data
*
* We know for sure that our structure is only made of 16bit fields
*/
static void fd_calib_cpu_to_endianess(struct fd_calibration *calib)
{
int i;
calib->magic = cpu_to_be32(calib->magic);
calib->size = cpu_to_be16(calib->size);
calib->version = cpu_to_be16(calib->version);
calib->date = cpu_to_be32(calib->date);
for (i = 0; i < ARRAY_SIZE(calib->frr_poly); i++)
calib->frr_poly[i] = cpu_to_be64(calib->frr_poly[i]);
for (i = 0; i < ARRAY_SIZE(calib->zero_offset); i++)
calib->zero_offset[i] = cpu_to_be32(calib->zero_offset[i]);
calib->tdc_zero_offset = cpu_to_be32(calib->tdc_zero_offset);
calib->vcxo_default_tune = cpu_to_be32(calib->vcxo_default_tune);
}
static int fd_verify_calib(struct device *msgdev, struct fd_calibration *calib)
{
uint32_t horig = 0, hash = 0;
horig = be32_to_cpu(calib->hash);
calib->hash = 0;
hash = jhash(calib, sizeof(*calib), 0);
if (hash != horig) {
dev_err(msgdev,
"Calibration hash %08x is wrong (expected %08x)\n",
hash, horig);
return -EINVAL;
}
calib->hash = hash;
return 0;
}
static void __fd_calib_write(struct fd_dev *fd, struct fd_calibration *calib)
{
struct fd_calibration *calib_good = calib;
int err;
err = fd_verify_calib(&fd->pdev->dev, calib);
if (err) {
dev_info(&fd->pdev->dev, "Apply Calibration Identity\n");
calib_good = &fd_calib_default;
} else {
fd_calib_endianess_to_cpus(calib);
if (calib->version < 3) {
dev_err(&fd->pdev->dev,
"Calibration version %i < 3: refusing to work\n. Use identity",
calib->version);
calib_good = &fd_calib_default;
}
}
memcpy(&fd->calib, calib_good, sizeof(fd->calib));
if (fd->verbose) {
int i;
dev_info(&fd->pdev->dev,
"calibration: version %i, date %08x\n",
fd->calib.version, fd->calib.date);
/* dump human-readable values */
dev_info(&fd->pdev->dev, "calib: magic 0x%08x\n",
fd->calib.magic);
for (i = 0; i < ARRAY_SIZE(fd->calib.frr_poly); i++)
dev_info(&fd->pdev->dev, "calib: poly[%i] = %lli\n",
i, (long long)fd->calib.frr_poly[i]);
for (i = 0; i < ARRAY_SIZE(fd->calib.zero_offset); i++)
dev_info(&fd->pdev->dev, "calib: offset[%i] = %li\n",
i, (long)fd->calib.zero_offset[i]);
dev_info(&fd->pdev->dev, "calib: tdc_offset %i\n",
fd->calib.tdc_zero_offset);
dev_info(&fd->pdev->dev, "calib: vcxo %i\n",
fd->calib.vcxo_default_tune);
}
}
static ssize_t fd_calib_write(struct file *file, struct kobject *kobj,
struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
{
struct device *dev = container_of(kobj, struct device, kobj);
struct fd_dev *fd = to_zio_dev(dev)->priv_d;
struct fd_calibration *calib = (struct fd_calibration *)buf;
if (off != 0 || count != sizeof(*calib))
return -EINVAL;
__fd_calib_write(fd, calib);
return count;
}
static ssize_t fd_calib_read(struct file *file, struct kobject *kobj,
struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
{
struct device *dev = container_of(kobj, struct device, kobj);
struct fd_dev *fd = to_zio_dev(dev)->priv_d;
struct fd_calibration *calib = (struct fd_calibration *) buf;
if (off != 0 || count < sizeof(fd->calib))
return -EINVAL;
memcpy(calib, &fd->calib, sizeof(fd->calib));
fd_calib_cpu_to_endianess(calib);
return count;
}
struct bin_attribute dev_attr_calibration = {
.attr = {
.name = "calibration_data",
.mode = 0644,
},
.size = sizeof(struct fd_calibration),
.write = fd_calib_write,
.read = fd_calib_read,
};
#define IPMI_FRU_SIZE 256
#define FD_EEPROM_SIZE (1024 * 8) /* 8KiB */
int fd_calib_init(struct fd_dev *fd)
{
struct fd_calibration calib;
const size_t data_len = FD_EEPROM_SIZE - IPMI_FRU_SIZE;
void *data;
off_t calib_offset;
int ret;
data = kmalloc(data_len, GFP_KERNEL);
if (!data)
goto err;
ret = fmc_slot_eeprom_read(fd->slot, data, IPMI_FRU_SIZE, data_len);
if (ret < 0) {
kfree(data);
goto err;
}
calib_offset = fd_calib_find_offset(data, data_len);
kfree(data);
if (calib_offset < 0)
goto err;
memcpy(&calib, data + calib_offset, sizeof(calib));
__fd_calib_write(fd, &calib);
return 0;
err:
dev_warn(&fd->pdev->dev,
"Failed to get calibration from EEPROM: using identity calibration\n");
memcpy(&fd->calib, &fd_calib_default, sizeof(fd->calib));
return 0;
}
void fd_calib_exit(struct fd_dev *fd)
{
}
/*
* core fine-delay driver (i.e., init and exit of the subsystems)
*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2 as published by the Free Software Foundation or, at your
* option, any later version.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/ipmi-fru.h>
#include <linux/fmc.h>
#include "fine-delay.h"
#include "hw/fd_main_regs.h"
/* Module parameters */
static int fd_verbose = 0;
module_param_named(verbose, fd_verbose, int, 0444);
#define FD_EEPROM_TYPE "at24c64"
/* FIXME: add parameters "file=" and "wrc=" like wr-nic-core does */
/**
* fd_do_reset
* The reset function (by Tomasz)
*
* This function can reset the entire mezzanine (FMC) or just
* the fine-delay core (CORE).
* In the reset register 0 means reset, 1 means normal operation.
*/
static void fd_do_reset(struct fd_dev *fd, int hw_reset)
{
if (hw_reset) {
/* clear RSTS_RST_FMC bit, set RSTS_RST_CORE bit*/
fd_writel(fd, FD_RSTR_LOCK_W(0xdead) | FD_RSTR_RST_CORE_MASK,
FD_REG_RSTR);
udelay(10000);
fd_writel(fd, FD_RSTR_LOCK_W(0xdead) | FD_RSTR_RST_CORE_MASK
| FD_RSTR_RST_FMC_MASK, FD_REG_RSTR);
/* TPS3307 supervisor needs time to de-assert master reset */
msleep(600);
return;
}
/* clear RSTS_RST_CORE bit, set RSTS_RST_FMC bit */
fd_writel(fd, FD_RSTR_LOCK_W(0xdead) | FD_RSTR_RST_FMC_MASK,
FD_REG_RSTR);
udelay(1000);
fd_writel(fd, FD_RSTR_LOCK_W(0xdead) | FD_RSTR_RST_FMC_MASK
| FD_RSTR_RST_CORE_MASK, FD_REG_RSTR);
udelay(1000);
}
/* Some init procedures to be intermixed with subsystems */
int fd_gpio_defaults(struct fd_dev *fd)
{
fd_gpio_dir(fd, FD_GPIO_TRIG_INTERNAL, FD_GPIO_OUT);
fd_gpio_set(fd, FD_GPIO_TRIG_INTERNAL);
fd_gpio_set(fd, FD_GPIO_OUTPUT_MASK);
fd_gpio_dir(fd, FD_GPIO_OUTPUT_MASK, FD_GPIO_OUT);
fd_gpio_dir(fd, FD_GPIO_TERM_EN, FD_GPIO_OUT);
fd_gpio_clr(fd, FD_GPIO_TERM_EN);
return 0;
}
int fd_reset_again(struct fd_dev *fd)
{
unsigned long j;
/* Reset the FD core once we have proper reference/TDC clocks */
fd_do_reset(fd, 0 /* not hw */);
j = jiffies + 2 * HZ;
while (time_before(jiffies, j)) {
if (fd_readl(fd, FD_REG_GCR) & FD_GCR_DDR_LOCKED)
break;
msleep(10);
}
if (time_after_eq(jiffies, j)) {
dev_err(&fd->pdev->dev,
"%s: timeout waiting for GCR lock bit\n", __func__);
return -EIO;
}
fd_do_reset(fd, 0 /* not hw */);
return 0;
}
/* This structure lists the various subsystems */
struct fd_modlist {
char *name;
int (*init)(struct fd_dev *);
void (*exit)(struct fd_dev *);
};
#define SUBSYS(x) { #x, fd_ ## x ## _init, fd_ ## x ## _exit }
static struct fd_modlist mods[] = {
SUBSYS(spi),
SUBSYS(gpio),
SUBSYS(pll),
SUBSYS(onewire),
{"gpio-default", fd_gpio_defaults},
{"reset-again", fd_reset_again},
SUBSYS(acam),
SUBSYS(time),
SUBSYS(zio),
};
static int fd_resource_validation(struct platform_device *pdev)
{
struct resource *r;
r = platform_get_resource(pdev, IORESOURCE_IRQ, FD_IRQ);
if (!r) {
dev_err(&pdev->dev,
"The Fine-Delay needs an interrupt number\n");
return -ENXIO;
}
if (!r->name) {
dev_err(&pdev->dev,
"The Fine-Delay IRQ needs to be named\n");
return -ENXIO;
}
r = platform_get_resource(pdev, IORESOURCE_MEM, FD_MEM_BASE);
if (!r) {
dev_err(&pdev->dev,
"The Fine-Delay needs base address\n");
return -ENXIO;
}
return 0;
}
#define FD_FMC_NAME "FmcDelay1ns4cha"
static bool fd_fmc_slot_is_valid(struct fd_dev *fd)
{
int ret;
void *fru = NULL;
char *fmc_name = NULL;
if (!fmc_slot_fru_valid(fd->slot)) {
dev_err(&fd->pdev->dev, "Can't identify FMC card: invalid FRU\n");
return -EINVAL;
}
fru = kmalloc(FRU_SIZE_MAX, GFP_KERNEL);
if (!fru)
return -ENOMEM;
ret = fmc_slot_eeprom_read(fd->slot, fru, 0x0, FRU_SIZE_MAX);
if (ret != FRU_SIZE_MAX) {
dev_err(&fd->pdev->dev, "Failed to read FRU header\n");
goto err;
}
fmc_name = fru_get_product_name(fru);
ret = strcmp(fmc_name, FD_FMC_NAME);
if (ret) {
dev_err(&fd->pdev->dev,
"Invalid FMC card: expectd '%s', found '%s'\n",
FD_FMC_NAME, fmc_name);
goto err;
}
kfree(fmc_name);
kfree(fru);
return true;
err:
kfree(fmc_name);
kfree(fru);
return false;
}
static int fd_endianess(struct fd_dev *fd)
{
uint32_t signature;
signature = ioread32(fd->fd_regs_base + FD_REG_IDR);
if (signature == FD_MAGIC_FPGA)
return 0;
signature = ioread32be(fd->fd_regs_base + FD_REG_IDR);
if (signature == FD_MAGIC_FPGA)
return 1;
return -1;
}
static int fd_memops_detect(struct fd_dev *fd)
{
int ret;
ret = fd_endianess(fd);
if (ret < 0) {
dev_err(&fd->pdev->dev, "Failed to detect endianess\n");
return -EINVAL;
}
if (ret) {
fd->memops.read = ioread32be;
fd->memops.write = iowrite32be;
} else {
fd->memops.read = ioread32;
fd->memops.write = iowrite32;
}
return 0;
}
/* probe and remove are called by the FMC bus core */
int fd_probe(struct platform_device *pdev)
{
struct fd_modlist *m;
struct fd_dev *fd;
struct device *dev = &pdev->dev;
int i, ret, ch, slot_nr;
struct resource *r;
ret = fd_resource_validation(pdev);
if (ret < 0)
return ret;
fd = devm_kzalloc(&pdev->dev, sizeof(*fd), GFP_KERNEL);
if (!fd)
return -ENOMEM;
platform_set_drvdata(pdev, fd);
fd->pdev = pdev;
fd->verbose = fd_verbose;
r = platform_get_resource(pdev, IORESOURCE_MEM, FD_MEM_BASE);
fd->fd_regs_base = ioremap(r->start, resource_size(r));
fd->fd_owregs_base = fd->fd_regs_base + 0x500;
spin_lock_init(&fd->lock);
ret = fd_memops_detect(fd);
if (ret)
goto err_memops;
slot_nr = fd_readl(fd, FD_REG_FMC_SLOT_ID) + 1;
fd->slot = fmc_slot_get(pdev->dev.parent->parent, slot_nr);
if (IS_ERR(fd->slot)) {
dev_err(&fd->pdev->dev,
"Can't find FMC slot %d err: %ld\n",
slot_nr, PTR_ERR(fd->slot));
goto out_fmc;
}
if (!fmc_slot_present(fd->slot)) {
dev_err(&fd->pdev->dev,
"Can't identify FMC card: missing card\n");
goto out_fmc_pre;
}
if (strcmp(fmc_slot_eeprom_type_get(fd->slot), FD_EEPROM_TYPE)) {
dev_warn(&fd->pdev->dev,
"use non standard EERPOM type \"%s\"\n",
FD_EEPROM_TYPE);
ret = fmc_slot_eeprom_type_set(fd->slot, FD_EEPROM_TYPE);
if (ret < 0) {
dev_err(&fd->pdev->dev,
"Failed to change EEPROM type to \"%s\"",
FD_EEPROM_TYPE);
goto out_fmc_eeprom;
}
}
if(!fd_fmc_slot_is_valid(fd))
goto out_fmc_err;
ret = fd_calib_init(fd);
if (ret < 0)
goto err_calib;;
/* First, hardware reset */
fd_do_reset(fd, 1);
/* init all subsystems */
for (i = 0, m = mods; i < ARRAY_SIZE(mods); i++, m++) {
dev_dbg(dev, "%s: Calling init for \"%s\"\n", __func__,
m->name);
ret = m->init(fd);
if (ret < 0) {
dev_err(dev, "%s: error initializing %s\n", __func__,
m->name);
goto err;
}
}
/* Finally, enable the input emgine */
ret = fd_irq_init(fd);
if (ret < 0)
goto err;
set_bit(FD_FLAG_INITED, &fd->flags);
/* set all output enable stages */
for (ch = 1; ch <= FD_CH_NUMBER; ch++)
fd_gpio_set(fd, FD_GPIO_OUTPUT_EN(ch));
return 0;
err:
while (--m, --i >= 0)
if (m->exit)
m->exit(fd);
fd_calib_exit(fd);
err_calib:
out_fmc_err:
out_fmc_eeprom:
out_fmc_pre:
fmc_slot_put(fd->slot);
out_fmc:
err_memops:
iounmap(fd->fd_regs_base);
devm_kfree(&pdev->dev, fd);
platform_set_drvdata(pdev, NULL);
return ret;
}
int fd_remove(struct platform_device *pdev)
{
struct fd_modlist *m;
struct fd_dev *fd = platform_get_drvdata(pdev);
int i = ARRAY_SIZE(mods);
if (!test_bit(FD_FLAG_INITED, &fd->flags)) /* FIXME: ditch this */
return 0; /* No init, no exit */
fd_irq_exit(fd);
while (--i >= 0) {
m = mods + i;
if (m->exit)
m->exit(fd);
}
fd_calib_exit(fd);
iounmap(fd->fd_regs_base);
fmc_slot_put(fd->slot);
return 0;
}
static const struct platform_device_id fd_id[] = {
{
.name = "fmc-fdelay-tdc",
.driver_data = FD_VER_TDC,
},
/* TODO we should support different version */
};
static struct platform_driver fd_platform_driver = {
.driver = {
.name = KBUILD_MODNAME,
},
.probe = fd_probe,
.remove = fd_remove,
.id_table = fd_id,
};
static int fd_init(void)
{
int ret;
ret = fd_zio_register();
if (ret < 0)
return ret;
ret = platform_driver_register(&fd_platform_driver);
if (ret < 0) {
fd_zio_unregister();
return ret;
}
return 0;
}
static void fd_exit(void)
{
platform_driver_unregister(&fd_platform_driver);
fd_zio_unregister();
}
module_init(fd_init);
module_exit(fd_exit);
MODULE_VERSION(VERSION);
MODULE_LICENSE("GPL and additional rights"); /* LGPL */
ADDITIONAL_VERSIONS;
/*\
* ZIO interface for the fine-delay driver
*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2 as published by the Free Software Foundation or, at your
* option, any later version.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
#include <linux/bitops.h>
#include <linux/spinlock.h>
#include <linux/io.h>
#include <linux/zio.h>
#include <linux/zio-buffer.h>
#include <linux/zio-trigger.h>
#include "fine-delay.h"
#include "hw/fd_main_regs.h"
#include "hw/fd_channel_regs.h"
static int fd_sw_fifo_len = FD_SW_FIFO_LEN;
module_param_named(fifo_len, fd_sw_fifo_len, int, 0444);
/* Subtract an offset (used for the input timestamp) */
static void fd_ts_sub(struct fd_time *t, uint64_t pico)
{
uint32_t coarse, frac;
/* FIXME: we really need to pre-convert pico to internal repres. */
fd_split_pico(pico, &coarse, &frac);
if (t->frac >= frac) {
t->frac -= frac;
} else {
t->frac = 4096 + t->frac - frac;
coarse++;
}
if (t->coarse >= coarse) {
t->coarse -= coarse;
} else {
t->coarse = 125*1000*1000 + t->coarse - coarse;
t->utc--;
}
}
static void fd_ts_add(struct fd_time *t, int64_t pico)
{
uint32_t coarse, frac;
/* FIXME: we really need to pre-convert pico to internal repres. */
if (pico < 0) {
fd_ts_sub(t, -pico);
return;
}
fd_split_pico(pico, &coarse, &frac);
t->frac += frac;
t->coarse += coarse;
if (t->frac >= 4096) {
t->frac -= 4096;
t->coarse++;
}
if (t->coarse >= 125*1000*1000) {
t->coarse -= 125*1000*1000;
t->utc++;
}
}
static inline void fd_normalize_time(struct fd_dev *fd, struct fd_time *t)
{
/* The coarse count may be negative, because of how it works */
if (t->coarse & (1<<27)) { // coarse is 28 bits
/* we may get 0xfff.ffef..0xffff.ffff -- 125M == 0x773.5940 */
t->coarse += 125000000;
t->coarse &= 0xfffffff;
t->utc--;
} else if(t->coarse >= 125000000) {
t->coarse -= 125000000;
t->utc++;
}
fd_ts_add(t, fd->calib.tdc_zero_offset);
fd_ts_add(t, fd->tdc_user_offset);
}
/* This is called from outside, too */
int fd_read_sw_fifo(struct fd_dev *fd, struct zio_channel *chan)
{
struct zio_control *ctrl;
struct zio_ti *ti = chan->cset->ti;
uint32_t *v;
int i, j;
struct fd_time t, *tp;
unsigned long flags;
if (fd->sw_fifo.tail == fd->sw_fifo.head)
return -EAGAIN;
/*
* Proceed even if no active block is there. The buffer may be
* full, but we need to keep the trigger armed for next time,
* so deal with data and return success. If we -EAGAIN when
* !chan->active_block is null, we'll miss an irq to restar the loop.
*/
/* Copy the sample to a local variable, to release the lock soon */
spin_lock_irqsave(&fd->lock, flags);
i = fd->sw_fifo.tail % fd_sw_fifo_len;
t = fd->sw_fifo.t[i];
fd->sw_fifo.tail++;
spin_unlock_irqrestore(&fd->lock, flags);
fd_normalize_time(fd, &t);
/* Write the timestamp in the trigger, it will reach the control */
ti->tstamp.tv_sec = t.utc;
ti->tstamp.tv_nsec = t.coarse * 8;
ti->tstamp_extra = t.frac;
/*
* This is different than it was. We used to fill the active block,
* but now zio copies chan->current_ctrl at a later time, so we
* must fill _those_ attributes instead
*/
/* The input data is written to attribute values in the active block. */
ctrl = chan->current_ctrl;
v = ctrl->attr_channel.ext_val;
v[FD_ATTR_TDC_UTC_H] = t.utc >> 32;
v[FD_ATTR_TDC_UTC_L] = t.utc;
v[FD_ATTR_TDC_COARSE] = t.coarse;
v[FD_ATTR_TDC_FRAC] = t.frac;
v[FD_ATTR_TDC_SEQ] = t.seq_id;
v[FD_ATTR_TDC_CHAN] = t.channel;
v[FD_ATTR_TDC_FLAGS] = fd->tdc_flags;
v[FD_ATTR_TDC_OFFSET] = fd->calib.tdc_zero_offset;
v[FD_ATTR_TDC_USER_OFF] = fd->tdc_user_offset;
/* We also need a copy within the device, so sysfs can read it */
memcpy(fd->tdc_attrs, v + FD_ATTR_DEV__LAST, sizeof(fd->tdc_attrs));
if (ctrl->ssize == 0) /* normal TDC device: no data */
return 0;
/*
* If we are returning raw data in the payload, cluster as many
* samples as they fit, or as many as the fifo has. If a block is there.
*/
if (!chan->active_block)
return 0;
tp = chan->active_block->data;
*tp++ = t; /* already normalized, above */
for (j = 1; j < ctrl->nsamples; j++, tp++) {
spin_lock_irqsave(&fd->lock, flags);
if (fd->sw_fifo.tail == fd->sw_fifo.head) {
spin_unlock_irqrestore(&fd->lock, flags);
break;
}
i = fd->sw_fifo.tail % fd_sw_fifo_len;
*tp = fd->sw_fifo.t[i];
fd->sw_fifo.tail++;
spin_unlock_irqrestore(&fd->lock, flags);
fd_normalize_time(fd, tp);
}
ctrl->nsamples = j;
chan->active_block->datalen = j * ctrl->ssize;
return 0;
}
/* This is local: reads the hw fifo and stores to the sw fifo */
static int fd_read_hw_fifo(struct fd_dev *fd)
{
uint32_t reg;
struct fd_time *t;
unsigned long flags;
signed long diff;
if ((fd_readl(fd, FD_REG_TSBCR) & FD_TSBCR_EMPTY))
return -EAGAIN;
spin_lock_irqsave(&fd->lock, flags);
t = fd->sw_fifo.t;
t += fd->sw_fifo.head % fd_sw_fifo_len;
/* Fetch the fifo entry to registers, so we can read them */
fd_writel(fd, FD_TSBR_ADVANCE_ADV, FD_REG_TSBR_ADVANCE);
/* Read input data into the sofware fifo */
t->utc = fd_readl(fd, FD_REG_TSBR_SECH) & 0xff;
t->utc <<= 32;
t->utc |= fd_readl(fd, FD_REG_TSBR_SECL);
t->coarse = fd_readl(fd, FD_REG_TSBR_CYCLES) & 0xfffffff;
reg = fd_readl(fd, FD_REG_TSBR_FID);
t->frac = FD_TSBR_FID_FINE_R(reg);
t->channel = FD_TSBR_FID_CHANNEL_R(reg);
t->seq_id = FD_TSBR_FID_SEQID_R(reg);
/* Then, increment head and make some checks */
diff = fd->sw_fifo.head - fd->sw_fifo.tail;
fd->sw_fifo.head++;
if (diff >= fd_sw_fifo_len)
fd->sw_fifo.tail += fd_sw_fifo_len / 2;
spin_unlock_irqrestore(&fd->lock, flags);
BUG_ON(diff < 0);
if (diff >= fd_sw_fifo_len)
dev_dbg(&fd->pdev->dev, "Fifo overflow: "
" dropped %i samples (%li -> %li == %li)\n",
fd_sw_fifo_len / 2,
fd->sw_fifo.tail, fd->sw_fifo.head, diff);
return 0;
}
/*
* We have a timer, used to poll for input samples, until the interrupt
* is there. A timer duration of 0 selects the interrupt.
*/
static int fd_timer_period_ms = 0;
module_param_named(timer_ms, fd_timer_period_ms, int, 0444);
static int fd_timer_period_jiffies; /* converted from ms at init time */
/* This acts as either a timer or an interrupt tasklet */
static void fd_tlet(unsigned long arg)
{
struct fd_dev *fd = (void *)arg;
struct zio_device *zdev = fd->zdev;
struct zio_channel *chan = zdev->cset[0].chan;
/* If we have no interrupt, read the hw fifo now */
if (fd_timer_period_ms) {
while (!fd_read_hw_fifo(fd))
;
mod_timer(&fd->fifo_timer, jiffies + fd_timer_period_jiffies);
}
/* FIXME: race condition */
if (!test_bit(FD_FLAG_INPUT_READY, &fd->flags))
return;
/* there is an active block, try reading an accumulated sample */
if (fd_read_sw_fifo(fd, chan) == 0) {
clear_bit(FD_FLAG_INPUT_READY, &fd->flags);
zio_trigger_data_done(chan->cset);
}
}
/*
* fd_irq_handler
* NOTE: TS_BUF_NOTEMPTY interrupt is level sensitive, it is cleared when
* you read the whole fifo buffer. It is useless to clear the interrupt
* in EIC_ISR
*/
irqreturn_t fd_irq_handler(int irq, void *arg)
{
struct fd_dev *fd = arg;
if ((fd_readl(fd, FD_REG_TSBCR) & FD_TSBCR_EMPTY))
goto out_unexpected; /* bah! */
/*
* We must empty the fifo in hardware, and ack at this point.
* I used to disable_irq() and empty the fifo in the tasklet,
* but it doesn't work because the hw request is still pending
*/
while (!fd_read_hw_fifo(fd))
;
tasklet_schedule(&fd->tlet);
out_unexpected:
return IRQ_HANDLED;
}
int fd_irq_init(struct fd_dev *fd)
{
int rv;
/* Check that the sw fifo size is a power of two */
if (fd_sw_fifo_len & (fd_sw_fifo_len - 1)) {
dev_err(&fd->pdev->dev,
"fifo len must be a power of 2 (not %d = 0x%x)\n",
fd_sw_fifo_len, fd_sw_fifo_len);
return -EINVAL;
}
fd->sw_fifo.t = kmalloc(fd_sw_fifo_len * sizeof(*fd->sw_fifo.t),
GFP_KERNEL);
if (!fd->sw_fifo.t)
return -ENOMEM;
/*
* According to the period, this can work with a timer (old way)
* or a custom tasklet (newer). Init both anyways, no harm is done.
*/
if (fd_timer_period_ms) {
setup_timer(&fd->fifo_timer, fd_tlet, (unsigned long)fd);
fd_timer_period_jiffies = msecs_to_jiffies(fd_timer_period_ms);
dev_dbg(&fd->pdev->dev,"Using a timer for input (%i ms)\n",
jiffies_to_msecs(fd_timer_period_jiffies));
mod_timer(&fd->fifo_timer, jiffies + fd_timer_period_jiffies);
} else {
struct resource *r;
/* Disable interrupts */
fd_writel(fd, ~0, FD_REG_EIC_IDR);
tasklet_init(&fd->tlet, fd_tlet, (unsigned long)fd);
r = platform_get_resource(fd->pdev, IORESOURCE_IRQ, FD_IRQ);
rv = request_any_context_irq(r->start, fd_irq_handler, 0,
r->name, fd);
if (rv < 0) {
dev_err(&fd->pdev->dev,
"Failed to request the interrupt %i (%i)\n",
platform_get_irq(fd->pdev, 0), rv);
goto out_irq_request;
}
/*
* Then, configure the hardware: first fine delay,
* then vic, and finally the carrier
*/
fd_writel(fd, FD_TSBIR_TIMEOUT_W(10) /* milliseconds */
|FD_TSBIR_THRESHOLD_W(15), /* samples */
FD_REG_TSBIR);
fd_writel(fd, FD_EIC_IER_TS_BUF_NOTEMPTY, FD_REG_EIC_IER);
}
/* let it run... */
fd_writel(fd, FD_GCR_INPUT_EN, FD_REG_GCR);
return 0;
out_irq_request:
kfree(fd->sw_fifo.t);
return rv;
}
void fd_irq_exit(struct fd_dev *fd)
{
/* Stop input */
fd_writel(fd, 0, FD_REG_GCR);
if (fd_timer_period_ms) {
del_timer_sync(&fd->fifo_timer);
} else {
fd_writel(fd, ~0, FD_REG_EIC_IDR);
free_irq(platform_get_irq(fd->pdev, 0), fd);
}
kfree(fd->sw_fifo.t);
}
/*
* ZIO interface for the fine-delay driver
*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2 as published by the Free Software Foundation or, at your
* option, any later version.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
#include <linux/bitops.h>
#include <linux/io.h>
#include <linux/zio.h>
#include <linux/zio-buffer.h>
#include <linux/zio-trigger.h>
#include "fine-delay.h"
#include "hw/fd_main_regs.h"
#include "hw/fd_channel_regs.h"
#define _RW_ (S_IRUGO | S_IWUGO) /* I want 80-col lines so this lazy thing */
static int fd_use_raw_tdc;
/* The user may want to use raw TDC registers for faster input */
module_param_named(raw_tdc, fd_use_raw_tdc, int, 0444);
/* The sample size. Mandatory, device-wide */
ZIO_ATTR_DEFINE_STD(ZIO_DEV, fd_zattr_dev_std) = {
ZIO_ATTR(zdev, ZIO_ATTR_NBITS, S_IRUGO, 0, 32), /* 32 bits. Really? */
};
/* Extended attributes for the device */
static struct zio_attribute fd_zattr_dev[] = {
ZIO_ATTR_EXT("version", S_IRUGO, FD_ATTR_DEV_VERSION,
FDELAY_VERSION_MAJ),
ZIO_ATTR_EXT("utc-h", _RW_, FD_ATTR_DEV_UTC_H, 0),
ZIO_ATTR_EXT("utc-l", _RW_, FD_ATTR_DEV_UTC_L, 0),
ZIO_ATTR_EXT("coarse", _RW_, FD_ATTR_DEV_COARSE, 0),
ZIO_ATTR_EXT("command", S_IWUGO, FD_ATTR_DEV_COMMAND, 0),
ZIO_ATTR_EXT("temperature", _RW_, FD_ATTR_DEV_TEMP, 0),
};
/* Extended attributes for the TDC (== input) cset */
static struct zio_attribute fd_zattr_input[] = {
ZIO_ATTR_EXT("utc-h", S_IRUGO, FD_ATTR_TDC_UTC_H, 0),
ZIO_ATTR_EXT("utc-l", S_IRUGO, FD_ATTR_TDC_UTC_L, 0),
ZIO_ATTR_EXT("coarse", S_IRUGO, FD_ATTR_TDC_COARSE, 0),
ZIO_ATTR_EXT("frac", S_IRUGO, FD_ATTR_TDC_FRAC, 0),
ZIO_ATTR_EXT("seq", S_IRUGO, FD_ATTR_TDC_SEQ, 0),
ZIO_ATTR_EXT("chan", S_IRUGO, FD_ATTR_TDC_CHAN, 0),
ZIO_ATTR_EXT("flags", _RW_, FD_ATTR_TDC_FLAGS, 0),
ZIO_ATTR_EXT("offset", _RW_, FD_ATTR_TDC_OFFSET, 0),
ZIO_ATTR_EXT("user-offset", _RW_, FD_ATTR_TDC_USER_OFF, 0),
};
/* Extended attributes for the output csets (most not-read-nor-write mode) */
static struct zio_attribute fd_zattr_output[] = {
ZIO_ATTR_EXT("mode", S_IRUGO, FD_ATTR_OUT_MODE, 0),
ZIO_ATTR_EXT("rep", S_IRUGO, FD_ATTR_OUT_REP, 0),
ZIO_ATTR_EXT("start-h", S_IRUGO, FD_ATTR_OUT_START_H, 0),
ZIO_ATTR_EXT("start-l", S_IRUGO, FD_ATTR_OUT_START_L, 0),
ZIO_ATTR_EXT("start-coarse", S_IRUGO, FD_ATTR_OUT_START_COARSE, 0),
ZIO_ATTR_EXT("start-fine", S_IRUGO, FD_ATTR_OUT_START_FINE, 0),
ZIO_ATTR_EXT("end-h", S_IRUGO, FD_ATTR_OUT_END_H, 0),
ZIO_ATTR_EXT("end-l", S_IRUGO, FD_ATTR_OUT_END_L, 0),
ZIO_ATTR_EXT("end-coarse", S_IRUGO, FD_ATTR_OUT_END_COARSE, 0),
ZIO_ATTR_EXT("end-fine", S_IRUGO, FD_ATTR_OUT_END_FINE, 0),
ZIO_ATTR_EXT("delta-l", S_IRUGO, FD_ATTR_OUT_DELTA_L, 0),
ZIO_ATTR_EXT("delta-coarse", S_IRUGO, FD_ATTR_OUT_DELTA_COARSE, 0),
ZIO_ATTR_EXT("delta-fine", S_IRUGO, FD_ATTR_OUT_DELTA_FINE, 0),
ZIO_ATTR_EXT("delay-offset", _RW_, FD_ATTR_OUT_DELAY_OFF, 0),
ZIO_ATTR_EXT("user-offset", _RW_, FD_ATTR_OUT_USER_OFF, 0),
};
/* This identifies if our "struct device" is device, input, output */
enum fd_devtype {
FD_TYPE_WHOLEDEV,
FD_TYPE_INPUT,
FD_TYPE_OUTPUT,
};
static enum fd_devtype __fd_get_type(struct device *dev)
{
struct zio_obj_head *head = to_zio_head(dev);
struct zio_cset *cset;
if (head->zobj_type == ZIO_DEV)
return FD_TYPE_WHOLEDEV;
cset = to_zio_cset(dev);
if (cset->index == 0)
return FD_TYPE_INPUT;
return FD_TYPE_OUTPUT;
}
/* TDC input attributes: only the user offset is special */
static int fd_zio_info_tdc(struct device *dev, struct zio_attribute *zattr,
uint32_t *usr_val)
{
struct zio_cset *cset;
struct fd_dev *fd;
cset = to_zio_cset(dev);
fd = cset->zdev->priv_d;
if (zattr->id == FD_ATTR_TDC_USER_OFF) {
*usr_val = fd->tdc_user_offset;
return 0;
}
if (zattr->id == FD_ATTR_TDC_FLAGS) {
*usr_val = fd->tdc_flags;
return 0;
}
/*
* Following code is about TDC values, for the last TDC event.
* For efficiency reasons at read_fifo() time, we store an
* array of integers instead of filling attributes, so here
* pick the values from our array.
*/
*usr_val = fd->tdc_attrs[FD_CSET_INDEX(zattr->id)];
return 0;
}
/* output channel: only the two offsets */
static int fd_zio_info_output(struct device *dev, struct zio_attribute *zattr,
uint32_t *usr_val)
{
struct zio_cset *cset;
struct fd_dev *fd;
int ch;
cset = to_zio_cset(dev);
ch = cset->index - 1;
fd = cset->zdev->priv_d;
if (zattr->id == FD_ATTR_OUT_DELAY_OFF) {
*usr_val = fd->calib.zero_offset[ch];
return 0;
}
if (zattr->id == FD_ATTR_OUT_USER_OFF) {
*usr_val = fd->ch_user_offset[ch];
return 0;
}
/* Reading the mode tells the current mode and whether it triggered or not */
if (zattr->id == FD_ATTR_OUT_MODE) {
uint32_t dcr = fd_ch_readl(fd, ch, FD_REG_DCR);
if(! (dcr & FD_DCR_ENABLE))
*usr_val = FD_OUT_MODE_DISABLED;
else if(dcr & FD_DCR_MODE)
*usr_val = FD_OUT_MODE_PULSE;
else
*usr_val = FD_OUT_MODE_DELAY;
if(dcr & FD_DCR_PG_TRIG)
*usr_val |= 0x80;
return 0;
}
/* readout of output config delays */
if (zattr->id == FD_ATTR_OUT_START_H) {
*usr_val = fd_ch_readl(fd, ch, FD_REG_U_STARTH);
return 0;
}
if (zattr->id == FD_ATTR_OUT_START_L) {
*usr_val = fd_ch_readl(fd, ch, FD_REG_U_STARTL);
return 0;
}
if (zattr->id == FD_ATTR_OUT_START_COARSE) {
*usr_val = fd_ch_readl(fd, ch, FD_REG_C_START);
return 0;
}
if (zattr->id == FD_ATTR_OUT_START_FINE) {
*usr_val = fd_ch_readl(fd, ch, FD_REG_F_START);
return 0;
}
if (zattr->id == FD_ATTR_OUT_END_H) {
*usr_val = fd_ch_readl(fd, ch, FD_REG_U_ENDH);
return 0;
}
if (zattr->id == FD_ATTR_OUT_END_L) {
*usr_val = fd_ch_readl(fd, ch, FD_REG_U_ENDL);
return 0;
}
if (zattr->id == FD_ATTR_OUT_END_COARSE) {
*usr_val = fd_ch_readl(fd, ch, FD_REG_C_END);
return 0;
}
if (zattr->id == FD_ATTR_OUT_END_FINE) {
*usr_val = fd_ch_readl(fd, ch, FD_REG_F_END);
return 0;
}
if (zattr->id == FD_ATTR_OUT_DELTA_L) {
*usr_val = fd_ch_readl(fd, ch, FD_REG_U_DELTA);
return 0;
}
if (zattr->id == FD_ATTR_OUT_DELTA_COARSE) {
*usr_val = fd_ch_readl(fd, ch, FD_REG_C_DELTA);
return 0;
}
if (zattr->id == FD_ATTR_OUT_DELTA_FINE) {
*usr_val = fd_ch_readl(fd, ch, FD_REG_F_DELTA);
return 0;
}
if (zattr->id == FD_ATTR_OUT_REP) {
uint32_t rcr = fd_ch_readl(fd, ch, FD_REG_RCR);
if(rcr & FD_RCR_CONT)
*usr_val = 0xffffffff;
else
*usr_val = FD_RCR_REP_CNT_R(rcr) + 1;
return 0;
}
return 0;
}
static int fd_wr_mode(struct fd_dev *fd, int on)
{
unsigned long flags;
uint32_t tcr;
spin_lock_irqsave(&fd->lock, flags);
tcr = fd_readl(fd, FD_REG_TCR);
if (on) {
fd_writel(fd, FD_TCR_WR_ENABLE, FD_REG_TCR);
set_bit(FD_FLAG_WR_MODE, &fd->flags);
} else {
fd_writel(fd, 0, FD_REG_TCR);
clear_bit(FD_FLAG_WR_MODE, &fd->flags);
/* not white-rabbit: write default to DAC for VCXO */
fd_spi_xfer(fd, FD_CS_DAC, 24,
fd->calib.vcxo_default_tune & 0xffff, NULL);
}
spin_unlock_irqrestore(&fd->lock, flags);
if(! (tcr & FD_TCR_WR_PRESENT))
return -EOPNOTSUPP;
else if( ! (tcr & FD_TCR_WR_LINK))
return -ENOLINK;
else
return 0;
}
static int fd_wr_query(struct fd_dev *fd)
{
int ena = test_bit(FD_FLAG_WR_MODE, &fd->flags);
if (!ena)
return -ENODEV;
if (! (fd_readl(fd, FD_REG_TCR) & FD_TCR_WR_LINK))
return -ENOLINK;
if (fd_readl(fd, FD_REG_TCR) & FD_TCR_WR_LOCKED)
return 0;
return -EAGAIN;
}
/* Overall and device-wide attributes: only get_time is special */
static int fd_zio_info_get(struct device *dev, struct zio_attribute *zattr,
uint32_t *usr_val)
{
struct fd_time t;
struct zio_device *zdev;
struct fd_dev *fd;
struct zio_attribute *attr;
if (__fd_get_type(dev) == FD_TYPE_INPUT)
return fd_zio_info_tdc(dev, zattr, usr_val);
if (__fd_get_type(dev) == FD_TYPE_OUTPUT)
return fd_zio_info_output(dev, zattr, usr_val);
/* reading temperature */
zdev = to_zio_dev(dev);
attr = zdev->zattr_set.ext_zattr;
fd = zdev->priv_d;
if (zattr->id == FD_ATTR_DEV_TEMP) {
if (fd->temp_ready)
{
attr[FD_ATTR_DEV_TEMP].value = fd->temp;
return 0;
} else
return -EAGAIN;
}
/* following is whole-dev */
if (zattr->id != FD_ATTR_DEV_UTC_H)
return 0;
/* reading utc-h calls an atomic get-time */
fd_time_get(fd, &t, NULL);
attr[FD_ATTR_DEV_UTC_H].value = t.utc >> 32;
attr[FD_ATTR_DEV_UTC_L].value = t.utc & 0xffffffff;
attr[FD_ATTR_DEV_COARSE].value = t.coarse;
return 0;
}
/* TDC input attributes: the flags */
static int fd_zio_conf_tdc(struct device *dev, struct zio_attribute *zattr,
uint32_t usr_val)
{
struct zio_cset *cset;
struct fd_dev *fd;
uint32_t reg;
int change;
cset = to_zio_cset(dev);
fd = cset->zdev->priv_d;
switch (zattr->id) {
case FD_ATTR_TDC_OFFSET:
fd->calib.tdc_zero_offset = usr_val;
goto out;
case FD_ATTR_TDC_USER_OFF:
fd->tdc_user_offset = usr_val;
goto out;
case FD_ATTR_TDC_FLAGS:
break; /* code below */
default:
goto out;
}
/* This code is only about FD_ATTR_TDC_FLAGS */
change = fd->tdc_flags ^ usr_val; /* old xor new */
/* No need to lock, as configuration is serialized by zio-core */
if (change & FD_TDCF_DISABLE_INPUT) {
reg = fd_readl(fd, FD_REG_GCR);
if (usr_val & FD_TDCF_DISABLE_INPUT)
reg &= ~FD_GCR_INPUT_EN;
else
reg |= FD_GCR_INPUT_EN;
fd_writel(fd, reg, FD_REG_GCR);
}
if (change & FD_TDCF_DISABLE_TSTAMP) {
reg = fd_readl(fd, FD_REG_TSBCR);
if (usr_val & FD_TDCF_DISABLE_TSTAMP)
reg &= ~FD_TSBCR_ENABLE;
else
reg |= FD_TSBCR_ENABLE;
fd_writel(fd, reg, FD_REG_TSBCR);
}
if (change & FD_TDCF_TERM_50) {
if (usr_val & FD_TDCF_TERM_50)
fd_gpio_set(fd, FD_GPIO_TERM_EN);
else
fd_gpio_clr(fd, FD_GPIO_TERM_EN);
}
out:
/* We need to store in the local array too (see info_tdc() above) */
fd->tdc_flags = usr_val;
return 0;
}
/* only the two offsets */
static int fd_zio_conf_output(struct device *dev, struct zio_attribute *zattr,
uint32_t usr_val)
{
struct zio_cset *cset;
struct fd_dev *fd;
int ch;
cset = to_zio_cset(dev);
fd = cset->zdev->priv_d;
ch = cset->index - 1;
if (zattr->id == FD_ATTR_OUT_DELAY_OFF) {
fd->calib.zero_offset[ch] = usr_val;
return 0;
}
if (zattr->id == FD_ATTR_OUT_USER_OFF) {
fd->ch_user_offset[ch] = usr_val;
return 0;
}
return 0;
}
/* conf_set dispatcher and and device-wide attributes */
static int fd_zio_conf_set(struct device *dev, struct zio_attribute *zattr,
uint32_t usr_val)
{
struct fd_time t;
struct zio_device *zdev;
struct fd_dev *fd;
struct zio_attribute *attr;
if (__fd_get_type(dev) == FD_TYPE_INPUT)
return fd_zio_conf_tdc(dev, zattr, usr_val);
if (__fd_get_type(dev) == FD_TYPE_OUTPUT)
return fd_zio_conf_output(dev, zattr, usr_val);
/* Remains: wholedev */
zdev = to_zio_dev(dev);
attr = zdev->zattr_set.ext_zattr;
fd = zdev->priv_d;
if (zattr->id == FD_ATTR_DEV_UTC_H) {
/* no changing of the time when WR is on */
if (test_bit(FD_FLAG_WR_MODE, &fd->flags))
return -EAGAIN;
/* writing utc-h calls an atomic set-time */
t.utc = (uint64_t)attr[FD_ATTR_DEV_UTC_H].value << 32;
t.utc |= attr[FD_ATTR_DEV_UTC_L].value;
t.coarse = attr[FD_ATTR_DEV_COARSE].value;
fd_time_set(fd, &t, NULL);
return 0;
}
/* Not command, nothing to do */
if (zattr->id != FD_ATTR_DEV_COMMAND)
return 0;
switch(usr_val) {
case FD_CMD_HOST_TIME:
/* can't change the time when WR is on */
if(test_bit(FD_FLAG_WR_MODE, &fd->flags))
return -EAGAIN;
return fd_time_set(fd, NULL, NULL);
case FD_CMD_WR_ENABLE:
return fd_wr_mode(fd, 1);
case FD_CMD_WR_DISABLE:
return fd_wr_mode(fd, 0);
case FD_CMD_WR_QUERY:
return fd_wr_query(fd);
case FD_CMD_DUMP_MCP:
return fd_dump_mcp(fd);
case FD_CMD_PURGE_FIFO:
fd_writel(fd, FD_TSBCR_PURGE | FD_TSBCR_RST_SEQ
| FD_TSBCR_CHAN_MASK_W(1) | FD_TSBCR_ENABLE,
FD_REG_TSBCR);
return 0;
default:
return -EINVAL;
}
}
/*
* We are over with attributes, now there's real I/O (part is in fd-irq.c)
*/
/* We need to change the time in attribute tuples, so here it is */
enum attrs {__UTC_H, __UTC_L, __COARSE, __FRAC}; /* the order of our attrs */
static void fd_attr_sub(uint32_t *a, uint32_t pico)
{
uint32_t coarse, frac;
fd_split_pico(pico, &coarse, &frac);
if (a[__FRAC] >= frac) {
a[__FRAC] -= frac;
} else {
a[__FRAC] += 4096;
a[__FRAC] -= frac;
coarse++;
}
if (a[__COARSE] >= coarse) {
a[__COARSE] -= coarse;
} else {
a[__COARSE] += 125*1000*1000;
a[__COARSE] -= coarse;
if (likely(a[__UTC_L] != 0)) {
a[__UTC_L]--;
} else {
a[__UTC_L] = ~0;
a[__UTC_H]--;
}
}
}
static void fd_attr_add(uint32_t *a, uint32_t pico)
{
uint32_t coarse, frac;
fd_split_pico(pico, &coarse, &frac);
a[__FRAC] += frac;
if (a[__FRAC] >= 4096) {
a[__FRAC] -= 4096;
coarse++;
}
a[__COARSE] += coarse;
if (a[__COARSE] >= 125*1000*1000) {
a[__COARSE] -= 125*1000*1000;
a[__UTC_L]++;
if (unlikely(a[__UTC_L] == 0))
a[__UTC_H]++;
}
}
void fd_apply_offset(uint32_t *a, int32_t off_pico)
{
if (off_pico) {
if (off_pico > 0)
fd_attr_add(a, off_pico);
else
fd_attr_sub(a, -off_pico);
}
}
/* Internal output engine */
static int __fd_zio_output(struct fd_dev *fd, int index1_4, uint32_t *attrs)
{
struct timespec delta, width, delay;
int ch = index1_4 - 1;
int mode = attrs[FD_ATTR_OUT_MODE];
int rep = attrs[FD_ATTR_OUT_REP];
int dcr = 0;
if (mode == FD_OUT_MODE_DELAY || mode == FD_OUT_MODE_DISABLED) {
if(rep < 0 || rep > 16) /* delay mode allows trains of 1 to 16 pulses. */
return 0;
/* check delay lower limits. FIXME: raise an alarm */
delay.tv_sec = attrs[FD_ATTR_OUT_START_L];
delay.tv_nsec = attrs[FD_ATTR_OUT_START_COARSE] * 8;
if (delay.tv_sec == 0 && delay.tv_nsec < 600)
return 0;
fd_apply_offset(attrs + FD_ATTR_OUT_START_H,
fd->calib.tdc_zero_offset);
fd_apply_offset(attrs + FD_ATTR_OUT_END_H,
fd->calib.tdc_zero_offset);
}
/* Apply offset to START timestamp */
fd_apply_offset(attrs + FD_ATTR_OUT_START_H,
fd->calib.zero_offset[ch]);
fd_apply_offset(attrs + FD_ATTR_OUT_START_H,
fd->ch_user_offset[ch]);
/* Apply offset to END timestamp */
fd_apply_offset(attrs + FD_ATTR_OUT_END_H,
fd->calib.zero_offset[ch]);
fd_apply_offset(attrs + FD_ATTR_OUT_END_H,
fd->ch_user_offset[ch]);
/* Update Fine Register with calibrated value */
fd_ch_writel(fd, ch, fd->ch[ch].frr_cur, FD_REG_FRR);
/* Write Pulse Start Absolute Time */
fd_ch_writel(fd, ch, attrs[FD_ATTR_OUT_START_H], FD_REG_U_STARTH);
fd_ch_writel(fd, ch, attrs[FD_ATTR_OUT_START_L], FD_REG_U_STARTL);
fd_ch_writel(fd, ch, attrs[FD_ATTR_OUT_START_COARSE], FD_REG_C_START);
fd_ch_writel(fd, ch, attrs[FD_ATTR_OUT_START_FINE], FD_REG_F_START);
/* Write Pulse End Absolute Time */
fd_ch_writel(fd, ch, attrs[FD_ATTR_OUT_END_H], FD_REG_U_ENDH);
fd_ch_writel(fd, ch, attrs[FD_ATTR_OUT_END_L], FD_REG_U_ENDL);
fd_ch_writel(fd, ch, attrs[FD_ATTR_OUT_END_COARSE], FD_REG_C_END);
fd_ch_writel(fd, ch, attrs[FD_ATTR_OUT_END_FINE], FD_REG_F_END);
/* Write clock cycles between rasing edges of output pulses */
fd_ch_writel(fd, ch, attrs[FD_ATTR_OUT_DELTA_L], FD_REG_U_DELTA);
fd_ch_writel(fd, ch, attrs[FD_ATTR_OUT_DELTA_COARSE], FD_REG_C_DELTA);
fd_ch_writel(fd, ch, attrs[FD_ATTR_OUT_DELTA_FINE], FD_REG_F_DELTA);
/*
* Configure the number of repetitions and the operational mode.
* The Fine delay always add an extra pulse to the repetition
* counter, so remove it on our side in order to produce exactly
* the number of pulses requested
*/
if (mode == FD_OUT_MODE_DELAY || mode == FD_OUT_MODE_DISABLED) {
/* Delay Mode */
dcr = 0;
fd_ch_writel(fd, ch, FD_RCR_REP_CNT_W(rep - 1)
| (rep < 0 ? FD_RCR_CONT : 0), FD_REG_RCR);
} else {
/* Pulse Mode */
dcr = FD_DCR_MODE; /* Set pulse mode */
fd_ch_writel(fd, ch, FD_RCR_REP_CNT_W(rep < 0 ? 0 : rep - 1)
| (rep < 0 ? FD_RCR_CONT : 0), FD_REG_RCR);
}
/*
* For narrowly spaced pulses, we don't have enough time to reload
* the tap number into the corresponding SY89295.
* Therefore, the width/spacing resolution is limited to 4 ns.
* We put the threshold at 200ns, i.e. when coarse == 25.
*
* Trivially it would be
* if((delta_ps - width_ps) < 200000 || (width_ps < 200000))
* dcr |= FD_DCR_NO_FINE;
*
* Most likely the calculation below fails with negatives, but
* with negative spacing we get no pulses, and fine is irrelevant
*/
delta.tv_sec = attrs[FD_ATTR_OUT_DELTA_L];
delta.tv_nsec = attrs[FD_ATTR_OUT_DELTA_COARSE] * 8;
width.tv_sec = ((uint64_t)(attrs[FD_ATTR_OUT_END_H]) << 32
| attrs[FD_ATTR_OUT_END_L])
- ((uint64_t)(attrs[FD_ATTR_OUT_START_H]) << 32
| attrs[FD_ATTR_OUT_START_L]);
if (attrs[FD_ATTR_OUT_END_COARSE] > attrs[FD_ATTR_OUT_START_COARSE]) {
width.tv_nsec = 8 * attrs[FD_ATTR_OUT_END_COARSE]
- 8 * attrs[FD_ATTR_OUT_START_COARSE];
} else {
width.tv_sec--;
width.tv_nsec = NSEC_PER_SEC
- 8 * attrs[FD_ATTR_OUT_START_COARSE]
+ 8 * attrs[FD_ATTR_OUT_END_COARSE];
}
/* delta = delta - width (i.e.: delta is the low-signal width */
delta.tv_sec -= width.tv_sec;
if (delta.tv_nsec > width.tv_nsec) {
delta.tv_nsec -= width.tv_nsec;
} else {
delta.tv_sec--;
delta.tv_nsec = NSEC_PER_SEC - width.tv_nsec + delta.tv_nsec;
}
/* finally check */
if (width.tv_sec == 0 && width.tv_nsec < 200)
dcr |= FD_DCR_NO_FINE;
if (delta.tv_sec == 0 && delta.tv_nsec < 200)
dcr |= FD_DCR_NO_FINE;
/* Configure Fine Delay output */
fd_ch_writel(fd, ch, dcr, FD_REG_DCR);
/* Update the time stamps according to start/end registers */
fd_ch_writel(fd, ch, dcr | FD_DCR_UPDATE, FD_REG_DCR);
/* Enable channel output */
if (mode == FD_OUT_MODE_DELAY) {
fd_ch_writel(fd, ch, dcr | FD_DCR_ENABLE, FD_REG_DCR);
} else if (mode == FD_OUT_MODE_PULSE) {
/* ... and arm the pulse generator */
fd_ch_writel(fd, ch, dcr | FD_DCR_ENABLE | FD_DCR_PG_ARM, FD_REG_DCR);
}
return 0;
}
/* This is called on user write */
static int fd_zio_output(struct zio_cset *cset)
{
int i;
struct fd_dev *fd;
struct zio_control *ctrl;
fd = cset->zdev->priv_d;
ctrl = zio_get_ctrl(cset->chan->active_block);
if (fd->verbose > 1) {
dev_info(&fd->pdev->dev,
"%s: attrs for cset %i: ", __func__, cset->index);
for (i = FD_ATTR_DEV__LAST; i < FD_ATTR_OUT__LAST; i++)
printk("%08x%c", ctrl->attr_channel.ext_val[i],
i == FD_ATTR_OUT__LAST -1 ? '\n' : ' ');
}
return __fd_zio_output(fd, cset->index, ctrl->attr_channel.ext_val);
}
/*
* The input method may return immediately, because input is
* asynchronous. The data_done callback is invoked when the block is
* full.
*/
static int fd_zio_input(struct zio_cset *cset)
{
struct fd_dev *fd;
fd = cset->zdev->priv_d;
/* Configure the device for input */
if (!test_bit(FD_FLAG_DO_INPUT, &fd->flags)) {
fd_writel(fd, FD_TSBCR_PURGE | FD_TSBCR_RST_SEQ, FD_REG_TSBCR);
fd_writel(fd, FD_TSBCR_CHAN_MASK_W(1) | FD_TSBCR_ENABLE,
FD_REG_TSBCR);
set_bit(FD_FLAG_DO_INPUT, &fd->flags);
}
/* Ready for input. If there's already something, return it now */
if (fd_read_sw_fifo(fd, cset->chan) == 0) {
return 0; /* don't call data_done, let the caller do it */
}
/* Mark the active block is valid, and return EAGAIN */
set_bit(FD_FLAG_INPUT_READY, &fd->flags);
return -EAGAIN;
}
/*
* The probe function receives a new zio_device, which is different from
* what we allocated (that one is the "hardwre" device) but has the
* same private data. So we make the link and return success.
*/
static int fd_zio_probe(struct zio_device *zdev)
{
struct fd_dev *fd;
int err;
/* link the new device from the fd structure */
fd = zdev->priv_d;
fd->zdev = zdev;
fd->tdc_attrs[FD_CSET_INDEX(FD_ATTR_TDC_OFFSET)] = \
fd->calib.tdc_zero_offset;
err = device_create_bin_file(&zdev->head.dev, &dev_attr_calibration);
if (err) {
dev_warn(&fd->pdev->dev,
"Cannot create sysfs attribute for calibration data\n");
return err;
}
/* We don't have csets at this point, so don't do anything more */
return 0;
}
static int fd_zio_remove(struct zio_device *zdev)
{
device_remove_bin_file(&zdev->head.dev, &dev_attr_calibration);
return 0;
}
/* Our sysfs operations to access internal settings */
static const struct zio_sysfs_operations fd_zio_s_op = {
.conf_set = fd_zio_conf_set,
.info_get = fd_zio_info_get,
};
/* We have 5 csets, since each output triggers separately */
static struct zio_cset fd_cset[] = {
{
ZIO_SET_OBJ_NAME("fd-input"),
.raw_io = fd_zio_input,
.n_chan = 1,
.ssize = 0,
.flags = ZIO_DIR_INPUT | ZIO_CSET_TYPE_TIME
| ZIO_CSET_SELF_TIMED,
.zattr_set = {
.ext_zattr = fd_zattr_input,
.n_ext_attr = ARRAY_SIZE(fd_zattr_input),
},
},
{
ZIO_SET_OBJ_NAME("fd-ch1"),
.raw_io = fd_zio_output,
.n_chan = 1,
.ssize = 0,
.flags = ZIO_DIR_OUTPUT | ZIO_CSET_TYPE_TIME,
.zattr_set = {
.ext_zattr = fd_zattr_output,
.n_ext_attr = ARRAY_SIZE(fd_zattr_output),
},
},
{
ZIO_SET_OBJ_NAME("fd-ch2"),
.raw_io = fd_zio_output,
.n_chan = 1,
.ssize = 0,
.flags = ZIO_DIR_OUTPUT | ZIO_CSET_TYPE_TIME,
.zattr_set = {
.ext_zattr = fd_zattr_output,
.n_ext_attr = ARRAY_SIZE(fd_zattr_output),
},
},
{
ZIO_SET_OBJ_NAME("fd-ch3"),
.raw_io = fd_zio_output,
.n_chan = 1,
.ssize = 0,
.flags = ZIO_DIR_OUTPUT | ZIO_CSET_TYPE_TIME,
.zattr_set = {
.ext_zattr = fd_zattr_output,
.n_ext_attr = ARRAY_SIZE(fd_zattr_output),
},
},
{
ZIO_SET_OBJ_NAME("fd-ch4"),
.raw_io = fd_zio_output,
.n_chan = 1,
.ssize = 0,
.flags = ZIO_DIR_OUTPUT | ZIO_CSET_TYPE_TIME,
.zattr_set = {
.ext_zattr = fd_zattr_output,
.n_ext_attr = ARRAY_SIZE(fd_zattr_output),
},
},
};
static struct zio_device fd_tmpl = {
.owner = THIS_MODULE,
.preferred_trigger = "user",
.s_op = &fd_zio_s_op,
.cset = fd_cset,
.n_cset = ARRAY_SIZE(fd_cset),
.zattr_set = {
.std_zattr= fd_zattr_dev_std,
.ext_zattr= fd_zattr_dev,
.n_ext_attr = ARRAY_SIZE(fd_zattr_dev),
},
};
static const struct zio_device_id fd_table[] = {
{"fd", &fd_tmpl},
{},
};
static struct zio_driver fd_zdrv = {
.driver = {
.name = "fd",
.owner = THIS_MODULE,
},
.id_table = fd_table,
.probe = fd_zio_probe,
.remove = fd_zio_remove,
/* Take the version from ZIO git sub-module */
.min_version = ZIO_VERSION(__ZIO_MIN_MAJOR_VERSION,
__ZIO_MIN_MINOR_VERSION,
0), /* Change it if you use new features from
a specific patch */
};
/* Register and unregister are used to set up the template driver */
int fd_zio_register(void)
{
int err;
if (fd_use_raw_tdc) {
/* Hack: change the input channel to return raw registers */
fd_cset[0].ssize = sizeof(struct fd_time);
fd_cset[0].flags = ZIO_DIR_INPUT | ZIO_CSET_TYPE_RAW;
}
err = zio_register_driver(&fd_zdrv);
if (err)
return err;
return 0;
}
void fd_zio_unregister(void)
{
zio_unregister_driver(&fd_zdrv);
/* FIXME */
}
/* preinitializes the outputs to some meaningful register values */
static void __fd_init_outputs(struct fd_dev *fd)
{
uint32_t attrs [ FD_ATTR_OUT__LAST];
int i;
attrs [ FD_ATTR_OUT_MODE ] = FD_OUT_MODE_DISABLED;
attrs [ FD_ATTR_OUT_REP ] = 1;
attrs [ FD_ATTR_OUT_START_H ] = 0;
attrs [ FD_ATTR_OUT_START_L ] = 0;
attrs [ FD_ATTR_OUT_START_COARSE ] = 75; /* 600 ns delay */
attrs [ FD_ATTR_OUT_START_FINE ] = 0;
attrs [ FD_ATTR_OUT_END_H ] = 0;
attrs [ FD_ATTR_OUT_END_L ] = 0;
attrs [ FD_ATTR_OUT_END_COARSE ] = 75 + 31; /* 250 ns width */
attrs [ FD_ATTR_OUT_END_FINE ] = 1024;
attrs [ FD_ATTR_OUT_DELTA_L ] = 0;
attrs [ FD_ATTR_OUT_DELTA_COARSE ] = 125; /* 1us ns period */
attrs [ FD_ATTR_OUT_DELTA_FINE ] = 0;
for (i = 1; i <= 4; i++)
__fd_zio_output(fd, i, attrs);
}
/* Init and exit are called for each FD card we have */
int fd_zio_init(struct fd_dev *fd)
{
int err = 0;
fd->hwzdev = zio_allocate_device();
if (IS_ERR(fd->hwzdev))
return PTR_ERR(fd->hwzdev);
/* Mandatory fields */
fd->hwzdev->owner = THIS_MODULE;
fd->hwzdev->priv_d = fd;
err = zio_register_device(fd->hwzdev, "fd", fd->pdev->id);
if (err) {
zio_free_device(fd->hwzdev);
return err;
}
__fd_init_outputs(fd);
return 0;
}
void fd_zio_exit(struct fd_dev *fd)
{
zio_unregister_device(fd->hwzdev);
zio_free_device(fd->hwzdev);
}
#ifndef __FINE_DELAY_H__
#define __FINE_DELAY_H__
enum fd_versions {
FD_VER_TDC = 0,
};
enum fd_mem_resource {
FD_MEM_BASE = 0,
};
enum fd_bus_resource {
FD_BUS_FMC_SLOT = 0,
};
enum fd_irq_resource {
FD_IRQ = 0,
};
#define FDELAY_VERSION_MAJ 2 /* version of the layout of registers */
/*
* ZIO concatenates device, cset and channel extended attributes in the 32
* values that are reported in the control block. So we are limited to
* 32 values at most, and the offset of cset attributes depends on the
* number of device attributes. For this reason, we reserve a few, in
* order not to increase the version number too often (we need to increase
* it when the layout of attributes changes in incompatible ways)
*/
/*
* NOTE: all tuples of 4 register must be enumerated in the proper order:
* utc-h, utc-l, coarse, frac __IN_THIS_ORDER__ because I make arith on them
*/
/* Device-wide ZIO attributes */
enum fd_zattr_dev_idx {
FD_ATTR_DEV_VERSION = 0,
FD_ATTR_DEV_UTC_H,
FD_ATTR_DEV_UTC_L,
FD_ATTR_DEV_COARSE,
FD_ATTR_DEV_COMMAND, /* see below for commands */
FD_ATTR_DEV_TEMP,
FD_ATTR_DEV_RESERVE_6,
FD_ATTR_DEV_RESERVE_7,
FD_ATTR_DEV__LAST,
};
enum fd_command {
FD_CMD_HOST_TIME = 0,
FD_CMD_WR_ENABLE,
FD_CMD_WR_DISABLE,
FD_CMD_WR_QUERY,
FD_CMD_DUMP_MCP,
FD_CMD_PURGE_FIFO = 5,
};
/* Input ZIO attributes (i.e. TDC attributes) */
enum fd_zattr_in_idx {
/* PLEASE check "NOTE:" above if you edit this*/
FD_ATTR_TDC_UTC_H = FD_ATTR_DEV__LAST,
FD_ATTR_TDC_UTC_L,
FD_ATTR_TDC_COARSE,
FD_ATTR_TDC_FRAC,
FD_ATTR_TDC_SEQ,
FD_ATTR_TDC_CHAN,
FD_ATTR_TDC_FLAGS, /* enable, termination, see below */
FD_ATTR_TDC_OFFSET,
FD_ATTR_TDC_USER_OFF,
FD_ATTR_TDC__LAST,
};
/* Names have been chosen so that 0 is the default at load time */
/**
* TDC flag to disable input pulse detection
* When disabled time-stamping and delay are impossible
*/
#define FD_TDCF_DISABLE_INPUT 1
/**
* TDC flag to disable input pulse time-stamping
* When disabled time-stamping are impossible, but delay will work
*/
#define FD_TDCF_DISABLE_TSTAMP 2
/**
* TDC flag to enable a 50Ohm termination
*/
#define FD_TDCF_TERM_50 4
/* Output ZIO attributes */
enum fd_zattr_out_idx {
FD_ATTR_OUT_MODE = FD_ATTR_DEV__LAST,
FD_ATTR_OUT_REP,
/* PLEASE check "NOTE:" above if you edit this*/
/* Start (or delay) is 4 registers */
FD_ATTR_OUT_START_H,
FD_ATTR_OUT_START_L,
FD_ATTR_OUT_START_COARSE,
FD_ATTR_OUT_START_FINE,
/* End (start + width) is 4 registers */
FD_ATTR_OUT_END_H,
FD_ATTR_OUT_END_L,
FD_ATTR_OUT_END_COARSE,
FD_ATTR_OUT_END_FINE,
/* Delta is 3 registers */
FD_ATTR_OUT_DELTA_L,
FD_ATTR_OUT_DELTA_COARSE,
FD_ATTR_OUT_DELTA_FINE,
/* The two offsets */
FD_ATTR_OUT_DELAY_OFF,
FD_ATTR_OUT_USER_OFF,
FD_ATTR_OUT__LAST,
};
enum fd_output_mode {
FD_OUT_MODE_DISABLED = 0,
FD_OUT_MODE_DELAY,
FD_OUT_MODE_PULSE,
};
/*
* Cset attributes are concatenated to device attributes in the control
* structure, but they start from 0 when allocate for the individual cset
*/
#define FD_CSET_INDEX(i) ((i) - FD_ATTR_DEV__LAST)
/*
* Internal time: the first three fields should be converted to zio time.
* This is exported to user space if raw_tdc is selected.
*/
struct fd_time {
uint64_t utc;
uint32_t coarse;
uint32_t frac;
uint32_t channel;
uint32_t seq_id;
};
struct fd_calibration { /* All of these are big endian */
uint32_t magic; /* magic ID: 0xf19ede1a */
uint32_t hash; /* jhash of it all, with this zeroed */
uint16_t size;
uint16_t version;
uint32_t date; /* hex: 0x20130410 = Apr 4th 2013 */
/* SY89295 delay/temperature polynomial coefficients */
int64_t frr_poly[3];
/* Output-to-internal-timebase offset in ps. Add to start/end output */
int32_t zero_offset[4];
/* TDC-to-internal-timebase offset in ps. Add to stamps and delays */
int32_t tdc_zero_offset;
/* Default DAC value for VCXO. Set during init and for local timing */
uint32_t vcxo_default_tune;
};
#ifdef __KERNEL__ /* All the rest is only of kernel users */
#include <linux/spinlock.h>
#include <linux/timer.h>
#include <linux/platform_device.h>
#include <linux/version.h>
#include <linux/interrupt.h>
#include <linux/fmc.h>
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,25)
#include <linux/math64.h>
#else
/* Hack to compile under 2.6.24: this comes from 2418f4f2 (Roman Zippel) */
static inline u64 div_u64_rem(u64 dividend, u32 divisor, u32 *remainder)
{
*remainder = do_div(dividend, divisor);
return dividend;
}
#endif
struct memory_ops {
u32 (*read)(void *addr);
void (*write)(u32 value, void *addr);
};
/* This is somehow generic, but I find no better place at this time */
#ifndef SET_HI32
# if BITS_PER_LONG > 32
# define SET_HI32(var, value) ((var) |= (value) << 32)
# define GET_HI32(var) ((var) >> 32)
# else
# define SET_HI32(var, value) ((var) |= 0)
# define GET_HI32(var) 0
# endif
#endif
/* Channels are called 1..4 in all docs. Internally it's 0..3 */
#define FD_CH_1 0
#define FD_CH_LAST 3
#define FD_CH_NUMBER 4
#define FD_CH_INT(i) ((i) - 1)
#define FD_CH_EXT(i) ((i) + 1)
#define FD_NUM_TAPS 1024 /* This is an hardware feature of SY89295U */
#define FD_CAL_STEPS 1024 /* This is a parameter: must be power of 2 */
#define FD_SW_FIFO_LEN 1024 /* Again, aa parameter: must be a power of 2 */
struct fd_ch {
/* Offset between FRR measured at known T at startup and poly-fitted */
uint32_t frr_offset;
/* Fine range register for each ch, current value (after T comp.) */
uint32_t frr_cur;
};
/* The software fifo is a circular buffer of fd_time structures */
struct fd_sw_fifo {
unsigned long head, tail;
struct fd_time *t;
};
/* This is the device we use all around */
struct fd_dev {
spinlock_t lock;
unsigned long flags;
void *fd_regs_base;
void *fd_owregs_base; /* regs_base + 0x500 */
struct memory_ops memops;
struct platform_device *pdev;
struct zio_device *zdev, *hwzdev;
struct timer_list fifo_timer;
struct timer_list temp_timer;
struct tasklet_struct tlet;
struct fd_calibration calib; /* a copy of what we have in flash */
struct fd_ch ch[FD_CH_NUMBER];
struct fmc_slot *slot;
uint32_t bin;
int acam_addr; /* cache of currently active addr */
uint8_t ds18_id[8];
unsigned long next_t;
int temp; /* temperature: scaled by 4 bits */
int temp_ready; /* temperature: measurement ready flag */
int verbose;
uint32_t tdc_attrs[FD_ATTR_TDC__LAST - FD_ATTR_DEV__LAST];
uint16_t mcp_iodir, mcp_olat;
struct fd_sw_fifo sw_fifo;
/* The following fields used to live in fd_calib */
int32_t tdc_user_offset;
int32_t ch_user_offset[4];
int32_t tdc_flags;
};
/* We act on flags using atomic ops, so flag is the number, not the mask */
enum fd_flags {
FD_FLAG_INITED = 0,
FD_FLAG_DO_INPUT,
FD_FLAG_INPUT_READY,
FD_FLAG_WR_MODE,
};
/* Split a pico value into coarse and frac */
static inline void fd_split_pico(uint64_t pico,
uint32_t *coarse, uint32_t *frac)
{
/* This works for less than 1s delays */
BUG_ON(pico > 1000ULL * NSEC_PER_SEC);
*coarse = div_u64_rem(pico, 8000, frac);
*frac = (*frac << 12) / 8000;
}
static inline u32 fd_ioread(struct fd_dev *fd, void *addr)
{
return fd->memops.read(addr);
}
static inline void fd_iowrite(struct fd_dev *fd,
u32 value, void *addr)
{
fd->memops.write(value, addr);
}
static inline uint32_t fd_readl(struct fd_dev *fd, unsigned long reg)
{
return fd_ioread(fd, (char *)fd->fd_regs_base + reg);
}
static inline void fd_writel(struct fd_dev *fd, uint32_t v, unsigned long reg)
{
fd_iowrite(fd, v, (char *)fd->fd_regs_base + reg);
}
static inline void __check_chan(int x)
{
BUG_ON(x < 0 || x > 3);
}
static inline uint32_t fd_ch_readl(struct fd_dev *fd, int ch,
unsigned long reg)
{
__check_chan(ch);
return fd_readl(fd, 0x100 + ch * 0x100 + reg);
}
static inline void fd_ch_writel(struct fd_dev *fd, int ch,
uint32_t v, unsigned long reg)
{
__check_chan(ch);
fd_writel(fd, v, 0x100 + ch * 0x100 + reg);
}
#define FD_MAGIC_FPGA 0xf19ede1a /* FD_REG_IDR content */
/* Values for the configuration of the acam PLL. Can be changed */
#define ACAM_DESIRED_BIN 80.9553
#define ACAM_CLOCK_FREQ_KHZ 31250
/* ACAM TDC operation modes */
enum fd_acam_modes {
ACAM_RMODE,
ACAM_IMODE,
ACAM_GMODE
};
/*
* You can change the following value to have a pll with smaller divisor,
* at the cost of potentially less precision in the desired bin value.
*/
#define ACAM_MAX_REFDIV 7
#define ACAM_MASK ((1<<29) - 1) /* 28 bits */
/* SPI Bus chip selects */
#define FD_CS_DAC 0 /* DAC for VCXO */
#define FD_CS_PLL 1 /* AD9516 PLL */
#define FD_CS_GPIO 2 /* MCP23S17 GPIO */
/* MCP23S17 register addresses (only ones which are used by the lib) */
#define FD_MCP_IODIR 0x00
#define FD_MCP_IPOL 0x01
#define FD_MCP_IOCON 0x0a
#define FD_MCP_GPIO 0x12
#define FD_MCP_OLAT 0x14
/*
* MCP23S17 GPIO direction and meaning
* NOTE: outputs are called 1..4 to match hw schematics
*/
#define FD_GPIO_IN 0
#define FD_GPIO_OUT 1
static inline void __check_output(int x)
{
BUG_ON(x < 1 || x > 4);
}
#define FD_GPIO_TERM_EN 0x0001 /* Input terminator enable */
#define FD_GPIO_OUTPUT_EN(x) \
({__check_output(x); 1 << (6-(x));}) /* Output driver enable */
#define FD_GPIO_OUTPUT_MASK 0x003c /* Output driver enable */
#define FD_GPIO_TRIG_INTERNAL 0x0040 /* TDC trig (1=in, 1=fpga) */
#define FD_GPIO_CAL_DISABLE 0x0080 /* 0 enables calibration */
/* Functions exported by spi.c */
extern int fd_spi_xfer(struct fd_dev *fd, int ss, int num_bits,
uint32_t in, uint32_t *out);
extern int fd_spi_init(struct fd_dev *fd);
extern void fd_spi_exit(struct fd_dev *fd);
/* Functions exported by pll.c */
extern int fd_pll_init(struct fd_dev *fd);
extern void fd_pll_exit(struct fd_dev *fd);
/* Functions exported by onewire.c */
extern int fd_onewire_init(struct fd_dev *fd);
extern void fd_onewire_exit(struct fd_dev *fd);
extern int fd_read_temp(struct fd_dev *fd, int verbose);
/* Functions exported by acam.c */
extern int fd_acam_init(struct fd_dev *fd);
extern void fd_acam_exit(struct fd_dev *fd);
extern uint32_t acam_readl(struct fd_dev *fd, int reg);
extern void acam_writel(struct fd_dev *fd, int val, int reg);
/* Functions exported by calibrate.c, called within acam.c */
extern int fd_calibrate_outputs(struct fd_dev *fd);
extern void fd_update_calibration(unsigned long arg);
extern int fd_calib_period_s;
/* Functions exported by gpio.c */
extern int fd_gpio_init(struct fd_dev *fd);
extern void fd_gpio_exit(struct fd_dev *fd);
extern void fd_gpio_dir(struct fd_dev *fd, int pin, int dir);
extern void fd_gpio_val(struct fd_dev *fd, int pin, int val);
extern void fd_gpio_set_clr(struct fd_dev *fd, int pin, int set);
extern int fd_dump_mcp(struct fd_dev *fd);
#define fd_gpio_set(fd, pin) fd_gpio_set_clr((fd), (pin), 1)
#define fd_gpio_clr(fd, pin) fd_gpio_set_clr((fd), (pin), 0)
/* Functions exported by time.c */
extern int fd_time_init(struct fd_dev *fd);
extern void fd_time_exit(struct fd_dev *fd);
extern int fd_time_set(struct fd_dev *fd, struct fd_time *t,
struct timespec *ts);
extern int fd_time_get(struct fd_dev *fd, struct fd_time *t,
struct timespec *ts);
/* Functions exported by fd-zio.c */
extern int fd_zio_register(void);
extern void fd_zio_unregister(void);
extern int fd_zio_init(struct fd_dev *fd);
extern void fd_zio_exit(struct fd_dev *fd);
extern void fd_apply_offset(uint32_t *a, int32_t off_pico);
/* Functions exported by fd-irq.c */
struct zio_channel;
extern int fd_read_sw_fifo(struct fd_dev *fd, struct zio_channel *chan);
extern int fd_irq_init(struct fd_dev *fd);
extern void fd_irq_exit(struct fd_dev *fd);
/* Functions exported by fd-spec.c */
extern int fd_spec_init(void);
extern void fd_spec_exit(void);
/* Function exported by calibration.c */
extern int fd_calib_init(struct fd_dev *fd);
extern void fd_calib_exit(struct fd_dev *fd);
extern struct bin_attribute dev_attr_calibration;
#endif /* __KERNEL__ */
#endif /* __FINE_DELAY_H__ */
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2019 CERN (www.cern.ch)
* Author: Federico Vaga <federico.vaga@cern.ch>
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/mfd/core.h>
enum fd_spec_dev_offsets {
FD_SPEC_FDT_MEM_START = 0x0000E000,
FD_SPEC_FDT_MEM_END = 0x0000E1FF,
};
static int fd_spec_probe(struct platform_device *pdev)
{
static struct resource fd_spec_fdt_res[] = {
{
.name = "fmc-fdelay-tdc-mem",
.flags = IORESOURCE_MEM,
},
{
.name = "fmc-fdelay-tdc-irq",
.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL,
}
};
struct platform_device_info pdevinfo = {
.parent = &pdev->dev,
.name = "fmc-fdelay-tdc",
.id = PLATFORM_DEVID_AUTO,
.res = fd_spec_fdt_res,
.num_res = ARRAY_SIZE(fd_spec_fdt_res),
.data = NULL,
.size_data = 0,
.dma_mask = 0,
};
struct platform_device *pdev_child;
struct resource *rmem;
int irq;
rmem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!rmem) {
dev_err(&pdev->dev, "Missing memory resource\n");
return -EINVAL;
}
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "Missing IRQ number\n");
return -EINVAL;
}
fd_spec_fdt_res[0].parent = rmem;
fd_spec_fdt_res[0].start = rmem->start + FD_SPEC_FDT_MEM_START;
fd_spec_fdt_res[0].end = rmem->start + FD_SPEC_FDT_MEM_END;
fd_spec_fdt_res[1].start = irq;
pdev_child = platform_device_register_full(&pdevinfo);
if (IS_ERR(pdev_child))
return PTR_ERR(pdev_child);
platform_set_drvdata(pdev, pdev_child);
return 0;
}
static int fd_spec_remove(struct platform_device *pdev)
{
struct platform_device *pdev_child = platform_get_drvdata(pdev);
platform_device_unregister(pdev_child);
return 0;
}
/**
* List of supported platform
*/
enum fd_spec_version {
FD_SPEC_VER = 0,
};
static const struct platform_device_id fd_spec_id_table[] = {
{
.name = "fdelay-spec",
.driver_data = FD_SPEC_VER,
}, {
.name = "id:000010DC574F0001",
.driver_data = FD_SPEC_VER,
}, {
.name = "id:000010dc574f0001",
.driver_data = FD_SPEC_VER,
},
{},
};
static struct platform_driver fd_spec_driver = {
.driver = {
.name = "fdelay-spec",
.owner = THIS_MODULE,
},
.id_table = fd_spec_id_table,
.probe = fd_spec_probe,
.remove = fd_spec_remove,
};
module_platform_driver(fd_spec_driver);
MODULE_AUTHOR("Federico Vaga <federico.vaga@cern.ch>");
MODULE_LICENSE("GPL");
MODULE_VERSION(VERSION);
MODULE_DESCRIPTION("Driver for the SPEC Fine-Delay");
MODULE_DEVICE_TABLE(platform, fd_spec_id_table);
MODULE_SOFTDEP("pre: spec_fmc_carrier fmc-fine-delay");
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2019 CERN (www.cern.ch)
* Author: Federico Vaga <federico.vaga@cern.ch>
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/mfd/core.h>
enum fd_svec_dev_offsets {
FD_SVEC_FDT1_MEM_START = 0x0000E000,
FD_SVEC_FDT1_MEM_END = 0x0000E1FF,
FD_SVEC_FDT2_MEM_START = 0x0001E000,
FD_SVEC_FDT2_MEM_END = 0x0001E1FF,
};
/* MFD devices */
enum svec_fpga_mfd_devs_enum {
FD_SVEC_MFD_FDT1 = 0,
FD_SVEC_MFD_FDT2,
};
static struct resource fd_svec_fdt1_res[] = {
{
.name = "fmc-fdelay-tdc-mem.1",
.flags = IORESOURCE_MEM,
.start = FD_SVEC_FDT1_MEM_START,
.end = FD_SVEC_FDT1_MEM_END,
}, {
.name = "fmc-fdelay-tdc-irq.1",
.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL,
.start = 0,
.end = 0,
},
};
static struct resource fd_svec_fdt2_res[] = {
{
.name = "fmc-fdelay-tdc-mem.2",
.flags = IORESOURCE_MEM,
.start = FD_SVEC_FDT2_MEM_START,
.end = FD_SVEC_FDT2_MEM_END,
}, {
.name = "fmc-fdelay-tdc-irq.2",
.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL,
.start = 1,
.end = 1,
},
};
static const struct mfd_cell fd_svec_mfd_devs[] = {
[FD_SVEC_MFD_FDT1] = {
.name = "fmc-fdelay-tdc",
.platform_data = NULL,
.pdata_size = 0,
.num_resources = ARRAY_SIZE(fd_svec_fdt1_res),
.resources = fd_svec_fdt1_res,
},
[FD_SVEC_MFD_FDT2] = {
.name = "fmc-fdelay-tdc",
.platform_data = NULL,
.pdata_size = 0,
.num_resources = ARRAY_SIZE(fd_svec_fdt2_res),
.resources = fd_svec_fdt2_res,
},
};
static int fd_svec_probe(struct platform_device *pdev)
{
struct resource *rmem;
int irq;
rmem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!rmem) {
dev_err(&pdev->dev, "Missing memory resource\n");
return -EINVAL;
}
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "Missing IRQ number\n");
return -EINVAL;
}
/*
* We know that this design uses the HTVIC IRQ controller.
* This IRQ controller has a linear mapping, so it is enough
* to give the first one as input
*/
return mfd_add_devices(&pdev->dev, PLATFORM_DEVID_AUTO,
fd_svec_mfd_devs,
ARRAY_SIZE(fd_svec_mfd_devs),
rmem, irq, NULL);
}
static int fd_svec_remove(struct platform_device *pdev)
{
mfd_remove_devices(&pdev->dev);
return 0;
}
/**
* List of supported platform
*/
enum fd_svec_version {
FD_SVEC_VER = 0,
};
static const struct platform_device_id fd_svec_id_table[] = {
{
.name = "fdelay-svec",
.driver_data = FD_SVEC_VER,
}, {
.name = "id:000010DC574F0002",
.driver_data = FD_SVEC_VER,
}, {
.name = "id:000010dc574f0002",
.driver_data = FD_SVEC_VER,
},
{},
};
static struct platform_driver fd_svec_driver = {
.driver = {
.name = "fdelay-svec",
.owner = THIS_MODULE,
},
.id_table = fd_svec_id_table,
.probe = fd_svec_probe,
.remove = fd_svec_remove,
};
module_platform_driver(fd_svec_driver);
MODULE_AUTHOR("Federico Vaga <federico.vaga@cern.ch>");
MODULE_LICENSE("GPL");
MODULE_VERSION(VERSION);
MODULE_DESCRIPTION("Driver for the SVEC Double Fine-Delay");
MODULE_DEVICE_TABLE(platform, fd_svec_id_table);
MODULE_SOFTDEP("pre: svec_fmc_carrier fmc-fine-delay");
/*
* SPI access to fine-delay internals
*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2 as published by the Free Software Foundation or, at your
* option, any later version.
*/
#include <linux/io.h>
#include "fine-delay.h"
#define SPI_RETRIES 100
static int gpio_writel(struct fd_dev *fd, int val, int reg)
{
int rval = fd_spi_xfer(fd, FD_CS_GPIO, 24,
0x4e0000 | (reg << 8) | val, NULL);
return rval;
}
static int gpio_readl(struct fd_dev *fd, int reg)
{
uint32_t ret;
int err;
err = fd_spi_xfer(fd, FD_CS_GPIO, 24,
0x4f0000 | (reg << 8), &ret);
if (err < 0)
return err;
return ret & 0xff;
}
static int gpio_writel_with_retry(struct fd_dev *fd, int val, int reg)
{
int retries = SPI_RETRIES, rv;
while(retries--)
{
gpio_writel(fd, val, reg);
rv = gpio_readl(fd, reg);
if(rv >= 0 && (rv == val))
{
if(SPI_RETRIES-1-retries > 0)
dev_info(&fd->pdev->dev,
"%s: succeded after %d retries\n",
__func__, SPI_RETRIES - 1 - retries);
return 0;
}
}
return -EIO;
}
void fd_gpio_dir(struct fd_dev *fd, int mask, int dir)
{
fd->mcp_iodir &= ~mask;
if (dir == FD_GPIO_IN)
fd->mcp_iodir |= mask;
gpio_writel_with_retry(fd, (fd->mcp_iodir & 0xff), FD_MCP_IODIR);
gpio_writel_with_retry(fd, (fd->mcp_iodir >> 8), FD_MCP_IODIR+1);
}
void fd_gpio_val(struct fd_dev *fd, int mask, int values)
{
fd->mcp_olat &= ~mask;
fd->mcp_olat |= values;
gpio_writel_with_retry(fd, (fd->mcp_olat & 0xff), FD_MCP_OLAT);
gpio_writel_with_retry(fd, (fd->mcp_olat >> 8), FD_MCP_OLAT+1);
}
void fd_gpio_set_clr(struct fd_dev *fd, int mask, int set)
{
if (set)
fd_gpio_val(fd, mask, mask);
else
fd_gpio_val(fd, mask, 0);
}
int fd_gpio_init(struct fd_dev *fd)
{
int i, val;
struct device *dev = &fd->pdev->dev;
fd->mcp_iodir = 0xffff;
fd->mcp_olat = 0;
if (gpio_writel(fd, 0x00, FD_MCP_IOCON) < 0)
goto out;
/* Try to read and write a register to test the SPI connection */
for (val = 0xaa; val >= 0; val -= 0x11) {
if (gpio_writel(fd, val, FD_MCP_IPOL) < 0)
goto out;
i = gpio_readl(fd, FD_MCP_IPOL);
if (i < 0)
goto out;
if (i != val) {
dev_err(dev, "%s: Error in GPIO communication\n",
KBUILD_MODNAME);
dev_err(dev, " (got 0x%x, expected 0x%x)\n", i, val);
return -EIO;
}
}
/* last time we wrote 0, ok */
return 0;
out:
dev_err(dev, "%s: Error in SPI communication\n", KBUILD_MODNAME);
return -EIO;
}
void fd_gpio_exit(struct fd_dev *fd)
{
/* nothing to do */
}
int fd_dump_mcp(struct fd_dev *fd)
{
printk(KERN_DEBUG "MCP23S17 register dump\n");
printk(KERN_DEBUG "IOCON: 0x%02x\n", gpio_readl(fd, FD_MCP_IOCON));
printk(KERN_DEBUG "IODIRA: 0x%02x\n", gpio_readl(fd, FD_MCP_IODIR));
printk(KERN_DEBUG "IODIRB: 0x%02x\n", gpio_readl(fd, FD_MCP_IODIR+1));
printk(KERN_DEBUG "OLATA: 0x%02x\n", gpio_readl(fd, FD_MCP_OLAT));
printk(KERN_DEBUG "OLATB: 0x%02x\n", gpio_readl(fd, FD_MCP_OLAT+1));
return 0;
}
#ifndef __ACAM_GPX_H
#define __ACAM_GPX_H
#define AR0_ROsc (1<<0)
#define AR0_RiseEn0 (1<<1)
#define AR0_FallEn0 (1<<2)
#define AR0_RiseEn1 (1<<3)
#define AR0_FallEn1 (1<<4)
#define AR0_RiseEn2 (1<<5)
#define AR0_FallEn2 (1<<6)
#define AR0_HQSel (1<<7)
#define AR0_TRiseEn(port) (1<<(10+port))
#define AR0_TFallEn(port) (1<<(19+port))
#define AR1_Adj(chan, value) (((value) & 0xf) << (chan * 4))
#define AR2_GMode (1<<0)
#define AR2_IMode (1<<1)
#define AR2_RMode (1<<2)
#define AR2_Disable(chan) (1<<(3+chan))
#define AR2_Adj(chan, value) (((value)&0xf)<<(12+4*(chan-7)))
#define AR2_DelRise1(value) (((value)&0x3)<<(20))
#define AR2_DelFall1(value) (((value)&0x3)<<(22))
#define AR2_DelRise2(value) (((value)&0x3)<<(24))
#define AR2_DelFall2(value) (((value)&0x3)<<(26))
#define AR3_DelTx(chan, value) (((value)&0x3)<<(5 + (chan -1 ) * 2))
#define AR3_RaSpeed(chan, value) (((value)&0x3)<<(21 + (chan ) * 2))
#define AR4_RaSpeed(chan, value) (((value)&0x3)<<(10 + (chan-3) * 2))
#define AR3_Zero (0) // nothing interesting for the Fine Delay
#define AR4_StartTimer(value) ((value) & 0xff)
#define AR4_Quiet (1<<8)
#define AR4_MMode (1<<9)
#define AR4_MasterReset (1<<22)
#define AR4_PartialReset (1<<23)
#define AR4_AluTrigSoft (1<<24)
#define AR4_EFlagHiZN (1<<25)
#define AR4_MTimerStart (1<<26)
#define AR4_MTimerStop (1<<27)
#define AR5_StartOff1(value) ((value)&0x3ffff)
#define AR5_StopDisStart (1<<21)
#define AR5_StartDisStart (1<<22)
#define AR5_MasterAluTrig (1<<23)
#define AR5_PartialAluTrig (1<<24)
#define AR5_MasterOenTrig (1<<25)
#define AR5_PartialOenTrig (1<<26)
#define AR5_StartRetrig (1<<27)
#define AR6_Fill(value) ((value)&0xff)
#define AR6_StartOff2(value) (((value)&0x3ffff)<<8)
#define AR6_InSelECL (1<<26)
#define AR6_PowerOnECL (1<<27)
#define AR7_HSDiv(value) ((value)&0xff)
#define AR7_RefClkDiv(value) (((value)&0x7)<<8)
#define AR7_ResAdj (1<<11)
#define AR7_NegPhase (1<<12)
#define AR7_Track (1<<13)
#define AR7_MTimer(value) (((value) & 0x1ff)<<15)
#define AR14_16BitMode (1<<4)
#define AR8I_IFIFO1(reg) ((reg) & 0x1ffff)
#define AR8I_Slope1(reg) ((reg) & (1<<17) ? 1 : 0)
#define AR8I_StartN1(reg) (((reg) >> 18) & 0xff)
#define AR8I_ChaCode1(reg) (((reg) >> 26) & 0x3)
#define AR9I_IFIFO2(reg) ((reg) & 0x1ffff)
#define AR9I_Slope2(reg) ((reg) & (1<<17) ? 1 : 0)
#define AR9I_StartN2(reg) (((reg) >> 18) & 0xff)
#define AR9I_ChaCode2(reg) (((reg) >> 26) & 0x3)
#define AR8R_IFIFO1(reg) ((reg) & 0x3fffff)
#define AR9R_IFIFO2(reg) ((reg) & 0x3fffff)
#define AR11_StopCounter0(num) ((num) & 0xff)
#define AR11_StopCounter1(num) (((num) & 0xff) << 8)
#define AR11_HFifoErrU(num) (1 << (num+16))
#define AR11_IFifoErrU(num) (1 << (num+24))
#define AR11_NotLockErrU (1 << 26)
#define AR12_HFifoE (1<<11)
#define AR12_NotLocked (1<<10)
#endif
/*
Register definitions for slave core: Fine Delay Channel WB Slave
* File : /tmp/head.h
* Author : auto-generated by wbgen2 from hdl/rtl/fd_channel_wishbone_slave.wb
* Created : Wed Sep 11 17:18:46 2019
* Standard : ANSI C
THIS FILE WAS GENERATED BY wbgen2 FROM SOURCE FILE hdl/rtl/fd_channel_wishbone_slave.wb
DO NOT HAND-EDIT UNLESS IT'S ABSOLUTELY NECESSARY!
*/
#ifndef __WBGEN2_REGDEFS_FD_CHANNEL_WISHBONE_SLAVE_WB
#define __WBGEN2_REGDEFS_FD_CHANNEL_WISHBONE_SLAVE_WB
#ifdef __KERNEL__
#include <linux/types.h>
#else
#include <inttypes.h>
#endif
#if defined( __GNUC__)
#define PACKED __attribute__ ((packed))
#else
#error "Unsupported compiler?"
#endif
#ifndef __WBGEN2_MACROS_DEFINED__
#define __WBGEN2_MACROS_DEFINED__
#define WBGEN2_GEN_MASK(offset, size) (((1<<(size))-1) << (offset))
#define WBGEN2_GEN_WRITE(value, offset, size) (((value) & ((1<<(size))-1)) << (offset))
#define WBGEN2_GEN_READ(reg, offset, size) (((reg) >> (offset)) & ((1<<(size))-1))
#define WBGEN2_SIGN_EXTEND(value, bits) (((value) & (1<<bits) ? ~((1<<(bits))-1): 0 ) | (value))
#endif
/* definitions for register: Delay Control Register */
/* definitions for field: Enable channel in reg: Delay Control Register */
#define FD_DCR_ENABLE WBGEN2_GEN_MASK(0, 1)
/* definitions for field: Delay mode select in reg: Delay Control Register */
#define FD_DCR_MODE WBGEN2_GEN_MASK(1, 1)
/* definitions for field: Pulse generator arm in reg: Delay Control Register */
#define FD_DCR_PG_ARM WBGEN2_GEN_MASK(2, 1)
/* definitions for field: Pulse generator triggered in reg: Delay Control Register */
#define FD_DCR_PG_TRIG WBGEN2_GEN_MASK(3, 1)
/* definitions for field: Update delay/absolute trigger time in reg: Delay Control Register */
#define FD_DCR_UPDATE WBGEN2_GEN_MASK(4, 1)
/* definitions for field: Delay update done flag in reg: Delay Control Register */
#define FD_DCR_UPD_DONE WBGEN2_GEN_MASK(5, 1)
/* definitions for field: Force calibration delay in reg: Delay Control Register */
#define FD_DCR_FORCE_DLY WBGEN2_GEN_MASK(6, 1)
/* definitions for field: Disable fine part update in reg: Delay Control Register */
#define FD_DCR_NO_FINE WBGEN2_GEN_MASK(7, 1)
/* definitions for field: Force output high in reg: Delay Control Register */
#define FD_DCR_FORCE_HI WBGEN2_GEN_MASK(8, 1)
/* definitions for register: Fine Range Register */
/* definitions for register: Pulse start time / offset (MSB TAI seconds) */
/* definitions for register: Pulse start time / offset (LSB TAI seconds) */
/* definitions for register: Pulse start time / offset (8 ns cycles) */
/* definitions for register: Pulse start time / offset (fine part) */
/* definitions for register: Pulse end time / offset (MSB TAI seconds) */
/* definitions for register: Pulse end time / offset (LSB TAI seconds) */
/* definitions for register: Pulse end time / offset (8 ns cycles) */
/* definitions for register: Pulse end time / offset (fine part) */
/* definitions for register: Pulse spacing (TAI seconds) */
/* definitions for register: Pulse spacing (8 ns cycles) */
/* definitions for register: Pulse spacing (fine part) */
/* definitions for register: Repeat Count Register */
/* definitions for field: Repeat Count in reg: Repeat Count Register */
#define FD_RCR_REP_CNT_MASK WBGEN2_GEN_MASK(0, 16)
#define FD_RCR_REP_CNT_SHIFT 0
#define FD_RCR_REP_CNT_W(value) WBGEN2_GEN_WRITE(value, 0, 16)
#define FD_RCR_REP_CNT_R(reg) WBGEN2_GEN_READ(reg, 0, 16)
/* definitions for field: Continuous Waveform Mode in reg: Repeat Count Register */
#define FD_RCR_CONT WBGEN2_GEN_MASK(16, 1)
/* [0x0]: REG Delay Control Register */
#define FD_REG_DCR 0x00000000
/* [0x4]: REG Fine Range Register */
#define FD_REG_FRR 0x00000004
/* [0x8]: REG Pulse start time / offset (MSB TAI seconds) */
#define FD_REG_U_STARTH 0x00000008
/* [0xc]: REG Pulse start time / offset (LSB TAI seconds) */
#define FD_REG_U_STARTL 0x0000000c
/* [0x10]: REG Pulse start time / offset (8 ns cycles) */
#define FD_REG_C_START 0x00000010
/* [0x14]: REG Pulse start time / offset (fine part) */
#define FD_REG_F_START 0x00000014
/* [0x18]: REG Pulse end time / offset (MSB TAI seconds) */
#define FD_REG_U_ENDH 0x00000018
/* [0x1c]: REG Pulse end time / offset (LSB TAI seconds) */
#define FD_REG_U_ENDL 0x0000001c
/* [0x20]: REG Pulse end time / offset (8 ns cycles) */
#define FD_REG_C_END 0x00000020
/* [0x24]: REG Pulse end time / offset (fine part) */
#define FD_REG_F_END 0x00000024
/* [0x28]: REG Pulse spacing (TAI seconds) */
#define FD_REG_U_DELTA 0x00000028
/* [0x2c]: REG Pulse spacing (8 ns cycles) */
#define FD_REG_C_DELTA 0x0000002c
/* [0x30]: REG Pulse spacing (fine part) */
#define FD_REG_F_DELTA 0x00000030
/* [0x34]: REG Repeat Count Register */
#define FD_REG_RCR 0x00000034
#endif
/*
Register definitions for slave core: Fine Delay Main WB Slave
* File : fd_main_regs.h
* Author : auto-generated by wbgen2 from fd_main_wishbone_slave.wb
* Created : Fri Apr 24 22:09:52 2020
* Standard : ANSI C
THIS FILE WAS GENERATED BY wbgen2 FROM SOURCE FILE fd_main_wishbone_slave.wb
DO NOT HAND-EDIT UNLESS IT'S ABSOLUTELY NECESSARY!
*/
#ifndef __WBGEN2_REGDEFS_FD_MAIN_WISHBONE_SLAVE_WB
#define __WBGEN2_REGDEFS_FD_MAIN_WISHBONE_SLAVE_WB
#ifdef __KERNEL__
#include <linux/types.h>
#else
#include <inttypes.h>
#endif
#if defined( __GNUC__)
#define PACKED __attribute__ ((packed))
#else
#error "Unsupported compiler?"
#endif
#ifndef __WBGEN2_MACROS_DEFINED__
#define __WBGEN2_MACROS_DEFINED__
#define WBGEN2_GEN_MASK(offset, size) (((1<<(size))-1) << (offset))
#define WBGEN2_GEN_WRITE(value, offset, size) (((value) & ((1<<(size))-1)) << (offset))
#define WBGEN2_GEN_READ(reg, offset, size) (((reg) >> (offset)) & ((1<<(size))-1))
#define WBGEN2_SIGN_EXTEND(value, bits) (((value) & (1<<bits) ? ~((1<<(bits))-1): 0 ) | (value))
#endif
/* definitions for register: Reset Register */
/* definitions for field: State of the reset Line of the Mezzanine (EXT_RST_N pin) in reg: Reset Register */
#define FD_RSTR_RST_FMC_MASK WBGEN2_GEN_MASK(0, 1)
#define FD_RSTR_RST_FMC_SHIFT 0
#define FD_RSTR_RST_FMC_W(value) WBGEN2_GEN_WRITE(value, 0, 1)
#define FD_RSTR_RST_FMC_R(reg) WBGEN2_GEN_READ(reg, 0, 1)
/* definitions for field: State of the reset of the Fine Delay Core in reg: Reset Register */
#define FD_RSTR_RST_CORE_MASK WBGEN2_GEN_MASK(1, 1)
#define FD_RSTR_RST_CORE_SHIFT 1
#define FD_RSTR_RST_CORE_W(value) WBGEN2_GEN_WRITE(value, 1, 1)
#define FD_RSTR_RST_CORE_R(reg) WBGEN2_GEN_READ(reg, 1, 1)
/* definitions for field: Reset magic value in reg: Reset Register */
#define FD_RSTR_LOCK_MASK WBGEN2_GEN_MASK(16, 16)
#define FD_RSTR_LOCK_SHIFT 16
#define FD_RSTR_LOCK_W(value) WBGEN2_GEN_WRITE(value, 16, 16)
#define FD_RSTR_LOCK_R(reg) WBGEN2_GEN_READ(reg, 16, 16)
/* definitions for register: ID Register */
/* definitions for register: Global Control Register */
/* definitions for field: Bypass hardware TDC controller in reg: Global Control Register */
#define FD_GCR_BYPASS WBGEN2_GEN_MASK(0, 1)
/* definitions for field: Enable trigger input in reg: Global Control Register */
#define FD_GCR_INPUT_EN WBGEN2_GEN_MASK(1, 1)
/* definitions for field: PLL lock status in reg: Global Control Register */
#define FD_GCR_DDR_LOCKED WBGEN2_GEN_MASK(2, 1)
/* definitions for field: Mezzanine present in reg: Global Control Register */
#define FD_GCR_FMC_PRESENT WBGEN2_GEN_MASK(3, 1)
/* definitions for register: Timing Control Register */
/* definitions for field: DMTD Clock Status in reg: Timing Control Register */
#define FD_TCR_DMTD_STAT WBGEN2_GEN_MASK(0, 1)
/* definitions for field: WR Timing Enable in reg: Timing Control Register */
#define FD_TCR_WR_ENABLE WBGEN2_GEN_MASK(1, 1)
/* definitions for field: WR Timing Locked in reg: Timing Control Register */
#define FD_TCR_WR_LOCKED WBGEN2_GEN_MASK(2, 1)
/* definitions for field: WR Core Present in reg: Timing Control Register */
#define FD_TCR_WR_PRESENT WBGEN2_GEN_MASK(3, 1)
/* definitions for field: WR Core Time Ready in reg: Timing Control Register */
#define FD_TCR_WR_READY WBGEN2_GEN_MASK(4, 1)
/* definitions for field: WR Core Link Up in reg: Timing Control Register */
#define FD_TCR_WR_LINK WBGEN2_GEN_MASK(5, 1)
/* definitions for field: Capture Current Time in reg: Timing Control Register */
#define FD_TCR_CAP_TIME WBGEN2_GEN_MASK(6, 1)
/* definitions for field: Set Current Time in reg: Timing Control Register */
#define FD_TCR_SET_TIME WBGEN2_GEN_MASK(7, 1)
/* definitions for register: Time Register - TAI seconds (MSB) */
/* definitions for register: Time Register - TAI seconds (LSB) */
/* definitions for register: Time Register - sub-second 125 MHz clock cycles */
/* definitions for register: Host-driven TDC Data Register. */
/* definitions for register: Host-driven TDC Control/Status */
/* definitions for field: Write to TDC in reg: Host-driven TDC Control/Status */
#define FD_TDCSR_WRITE WBGEN2_GEN_MASK(0, 1)
/* definitions for field: Read from TDC in reg: Host-driven TDC Control/Status */
#define FD_TDCSR_READ WBGEN2_GEN_MASK(1, 1)
/* definitions for field: Empty flag in reg: Host-driven TDC Control/Status */
#define FD_TDCSR_EMPTY WBGEN2_GEN_MASK(2, 1)
/* definitions for field: Stop enable in reg: Host-driven TDC Control/Status */
#define FD_TDCSR_STOP_EN WBGEN2_GEN_MASK(3, 1)
/* definitions for field: Start disable in reg: Host-driven TDC Control/Status */
#define FD_TDCSR_START_DIS WBGEN2_GEN_MASK(4, 1)
/* definitions for field: Start enable in reg: Host-driven TDC Control/Status */
#define FD_TDCSR_START_EN WBGEN2_GEN_MASK(5, 1)
/* definitions for field: Stop disable in reg: Host-driven TDC Control/Status */
#define FD_TDCSR_STOP_DIS WBGEN2_GEN_MASK(6, 1)
/* definitions for field: Pulse <code>Alutrigger</code> line in reg: Host-driven TDC Control/Status */
#define FD_TDCSR_ALUTRIG WBGEN2_GEN_MASK(7, 1)
/* definitions for field: IDELAY CE (pulse) in reg: Host-driven TDC Control/Status */
#define FD_TDCSR_IDELAY_CE WBGEN2_GEN_MASK(8, 1)
/* definitions for register: Calibration register */
/* definitions for field: Generate calibration pulses (type 1 calibration) in reg: Calibration register */
#define FD_CALR_CAL_PULSE WBGEN2_GEN_MASK(0, 1)
/* definitions for field: PPS calibration output enable. in reg: Calibration register */
#define FD_CALR_CAL_PPS WBGEN2_GEN_MASK(1, 1)
/* definitions for field: Produce DDMTD calibration pattern (type 2 calibration) in reg: Calibration register */
#define FD_CALR_CAL_DMTD WBGEN2_GEN_MASK(2, 1)
/* definitions for field: Calibration pulse output select/mask in reg: Calibration register */
#define FD_CALR_PSEL_MASK WBGEN2_GEN_MASK(3, 4)
#define FD_CALR_PSEL_SHIFT 3
#define FD_CALR_PSEL_W(value) WBGEN2_GEN_WRITE(value, 3, 4)
#define FD_CALR_PSEL_R(reg) WBGEN2_GEN_READ(reg, 3, 4)
/* definitions for register: DMTD Input Tag Register */
/* definitions for field: DMTD Tag in reg: DMTD Input Tag Register */
#define FD_DMTR_IN_TAG_MASK WBGEN2_GEN_MASK(0, 31)
#define FD_DMTR_IN_TAG_SHIFT 0
#define FD_DMTR_IN_TAG_W(value) WBGEN2_GEN_WRITE(value, 0, 31)
#define FD_DMTR_IN_TAG_R(reg) WBGEN2_GEN_READ(reg, 0, 31)
/* definitions for field: DMTD Tag Ready in reg: DMTD Input Tag Register */
#define FD_DMTR_IN_RDY WBGEN2_GEN_MASK(31, 1)
/* definitions for register: DMTD Output Tag Register */
/* definitions for field: DMTD Tag in reg: DMTD Output Tag Register */
#define FD_DMTR_OUT_TAG_MASK WBGEN2_GEN_MASK(0, 31)
#define FD_DMTR_OUT_TAG_SHIFT 0
#define FD_DMTR_OUT_TAG_W(value) WBGEN2_GEN_WRITE(value, 0, 31)
#define FD_DMTR_OUT_TAG_R(reg) WBGEN2_GEN_READ(reg, 0, 31)
/* definitions for field: DMTD Tag Ready in reg: DMTD Output Tag Register */
#define FD_DMTR_OUT_RDY WBGEN2_GEN_MASK(31, 1)
/* definitions for register: Acam Scaling Factor Register */
/* definitions for register: Acam Timestamp Merging Control Register */
/* definitions for field: Coarse threshold in reg: Acam Timestamp Merging Control Register */
#define FD_ATMCR_C_THR_MASK WBGEN2_GEN_MASK(0, 8)
#define FD_ATMCR_C_THR_SHIFT 0
#define FD_ATMCR_C_THR_W(value) WBGEN2_GEN_WRITE(value, 0, 8)
#define FD_ATMCR_C_THR_R(reg) WBGEN2_GEN_READ(reg, 0, 8)
/* definitions for field: Fine threshold in reg: Acam Timestamp Merging Control Register */
#define FD_ATMCR_F_THR_MASK WBGEN2_GEN_MASK(8, 23)
#define FD_ATMCR_F_THR_SHIFT 8
#define FD_ATMCR_F_THR_W(value) WBGEN2_GEN_WRITE(value, 8, 23)
#define FD_ATMCR_F_THR_R(reg) WBGEN2_GEN_READ(reg, 8, 23)
/* definitions for register: Acam Start Offset Register */
/* definitions for field: Start Offset in reg: Acam Start Offset Register */
#define FD_ASOR_OFFSET_MASK WBGEN2_GEN_MASK(0, 23)
#define FD_ASOR_OFFSET_SHIFT 0
#define FD_ASOR_OFFSET_W(value) WBGEN2_GEN_WRITE(value, 0, 23)
#define FD_ASOR_OFFSET_R(reg) WBGEN2_GEN_READ(reg, 0, 23)
/* definitions for register: Raw Input Events Counter Register */
/* definitions for register: Tagged Input Events Counter Register */
/* definitions for register: Input Event Processing Delay Register */
/* definitions for field: Reset stats in reg: Input Event Processing Delay Register */
#define FD_IEPD_RST_STAT WBGEN2_GEN_MASK(0, 1)
/* definitions for field: Processing delay in reg: Input Event Processing Delay Register */
#define FD_IEPD_PDELAY_MASK WBGEN2_GEN_MASK(1, 8)
#define FD_IEPD_PDELAY_SHIFT 1
#define FD_IEPD_PDELAY_W(value) WBGEN2_GEN_WRITE(value, 1, 8)
#define FD_IEPD_PDELAY_R(reg) WBGEN2_GEN_READ(reg, 1, 8)
/* definitions for register: SPI Control Register */
/* definitions for field: Data in reg: SPI Control Register */
#define FD_SCR_DATA_MASK WBGEN2_GEN_MASK(0, 24)
#define FD_SCR_DATA_SHIFT 0
#define FD_SCR_DATA_W(value) WBGEN2_GEN_WRITE(value, 0, 24)
#define FD_SCR_DATA_R(reg) WBGEN2_GEN_READ(reg, 0, 24)
/* definitions for field: Select DAC in reg: SPI Control Register */
#define FD_SCR_SEL_DAC WBGEN2_GEN_MASK(24, 1)
/* definitions for field: Select PLL in reg: SPI Control Register */
#define FD_SCR_SEL_PLL WBGEN2_GEN_MASK(25, 1)
/* definitions for field: Select GPIO in reg: SPI Control Register */
#define FD_SCR_SEL_GPIO WBGEN2_GEN_MASK(26, 1)
/* definitions for field: Ready flag in reg: SPI Control Register */
#define FD_SCR_READY WBGEN2_GEN_MASK(27, 1)
/* definitions for field: Clock Polarity in reg: SPI Control Register */
#define FD_SCR_CPOL WBGEN2_GEN_MASK(28, 1)
/* definitions for field: Transfer Start in reg: SPI Control Register */
#define FD_SCR_START WBGEN2_GEN_MASK(29, 1)
/* definitions for register: Reference Clock Rate Register */
/* definitions for register: Timestamp Buffer Control Register */
/* definitions for field: Channel mask in reg: Timestamp Buffer Control Register */
#define FD_TSBCR_CHAN_MASK_MASK WBGEN2_GEN_MASK(0, 5)
#define FD_TSBCR_CHAN_MASK_SHIFT 0
#define FD_TSBCR_CHAN_MASK_W(value) WBGEN2_GEN_WRITE(value, 0, 5)
#define FD_TSBCR_CHAN_MASK_R(reg) WBGEN2_GEN_READ(reg, 0, 5)
/* definitions for field: Buffer enable in reg: Timestamp Buffer Control Register */
#define FD_TSBCR_ENABLE WBGEN2_GEN_MASK(5, 1)
/* definitions for field: Buffer purge in reg: Timestamp Buffer Control Register */
#define FD_TSBCR_PURGE WBGEN2_GEN_MASK(6, 1)
/* definitions for field: Reset timestamp sequence number in reg: Timestamp Buffer Control Register */
#define FD_TSBCR_RST_SEQ WBGEN2_GEN_MASK(7, 1)
/* definitions for field: Buffer full in reg: Timestamp Buffer Control Register */
#define FD_TSBCR_FULL WBGEN2_GEN_MASK(8, 1)
/* definitions for field: Buffer empty in reg: Timestamp Buffer Control Register */
#define FD_TSBCR_EMPTY WBGEN2_GEN_MASK(9, 1)
/* definitions for field: Buffer entries count in reg: Timestamp Buffer Control Register */
#define FD_TSBCR_COUNT_MASK WBGEN2_GEN_MASK(10, 12)
#define FD_TSBCR_COUNT_SHIFT 10
#define FD_TSBCR_COUNT_W(value) WBGEN2_GEN_WRITE(value, 10, 12)
#define FD_TSBCR_COUNT_R(reg) WBGEN2_GEN_READ(reg, 10, 12)
/* definitions for field: RAW readout mode enable in reg: Timestamp Buffer Control Register */
#define FD_TSBCR_RAW WBGEN2_GEN_MASK(22, 1)
/* definitions for register: Timestamp Buffer Interrupt Register */
/* definitions for field: IRQ timeout [milliseconds] in reg: Timestamp Buffer Interrupt Register */
#define FD_TSBIR_TIMEOUT_MASK WBGEN2_GEN_MASK(0, 10)
#define FD_TSBIR_TIMEOUT_SHIFT 0
#define FD_TSBIR_TIMEOUT_W(value) WBGEN2_GEN_WRITE(value, 0, 10)
#define FD_TSBIR_TIMEOUT_R(reg) WBGEN2_GEN_READ(reg, 0, 10)
/* definitions for field: Interrupt threshold in reg: Timestamp Buffer Interrupt Register */
#define FD_TSBIR_THRESHOLD_MASK WBGEN2_GEN_MASK(10, 12)
#define FD_TSBIR_THRESHOLD_SHIFT 10
#define FD_TSBIR_THRESHOLD_W(value) WBGEN2_GEN_WRITE(value, 10, 12)
#define FD_TSBIR_THRESHOLD_R(reg) WBGEN2_GEN_READ(reg, 10, 12)
/* definitions for register: Timestamp Buffer Readout Seconds Register (MSB) */
/* definitions for register: Timestamp Buffer Readout Seconds Register (LSB) */
/* definitions for register: Timestamp Buffer Readout Cycles Register */
/* definitions for register: Timestamp Buffer Readout Fine/Channel/Sequence ID Register */
/* definitions for field: Channel ID in reg: Timestamp Buffer Readout Fine/Channel/Sequence ID Register */
#define FD_TSBR_FID_CHANNEL_MASK WBGEN2_GEN_MASK(0, 4)
#define FD_TSBR_FID_CHANNEL_SHIFT 0
#define FD_TSBR_FID_CHANNEL_W(value) WBGEN2_GEN_WRITE(value, 0, 4)
#define FD_TSBR_FID_CHANNEL_R(reg) WBGEN2_GEN_READ(reg, 0, 4)
/* definitions for field: Fine Value (in phase units) in reg: Timestamp Buffer Readout Fine/Channel/Sequence ID Register */
#define FD_TSBR_FID_FINE_MASK WBGEN2_GEN_MASK(4, 12)
#define FD_TSBR_FID_FINE_SHIFT 4
#define FD_TSBR_FID_FINE_W(value) WBGEN2_GEN_WRITE(value, 4, 12)
#define FD_TSBR_FID_FINE_R(reg) WBGEN2_GEN_READ(reg, 4, 12)
/* definitions for field: Timestamp Sequence ID in reg: Timestamp Buffer Readout Fine/Channel/Sequence ID Register */
#define FD_TSBR_FID_SEQID_MASK WBGEN2_GEN_MASK(16, 16)
#define FD_TSBR_FID_SEQID_SHIFT 16
#define FD_TSBR_FID_SEQID_W(value) WBGEN2_GEN_WRITE(value, 16, 16)
#define FD_TSBR_FID_SEQID_R(reg) WBGEN2_GEN_READ(reg, 16, 16)
/* definitions for register: I2C Bit-banged IO Register */
/* definitions for field: SCL Line out in reg: I2C Bit-banged IO Register */
#define FD_I2CR_SCL_OUT WBGEN2_GEN_MASK(0, 1)
/* definitions for field: SDA Line out in reg: I2C Bit-banged IO Register */
#define FD_I2CR_SDA_OUT WBGEN2_GEN_MASK(1, 1)
/* definitions for field: SCL Line in in reg: I2C Bit-banged IO Register */
#define FD_I2CR_SCL_IN WBGEN2_GEN_MASK(2, 1)
/* definitions for field: SDA Line in in reg: I2C Bit-banged IO Register */
#define FD_I2CR_SDA_IN WBGEN2_GEN_MASK(3, 1)
/* definitions for register: Test/Debug Register 1 */
/* definitions for field: VCXO Frequency in reg: Test/Debug Register 1 */
#define FD_TDER1_VCXO_FREQ_MASK WBGEN2_GEN_MASK(0, 32)
#define FD_TDER1_VCXO_FREQ_SHIFT 0
#define FD_TDER1_VCXO_FREQ_W(value) WBGEN2_GEN_WRITE(value, 0, 32)
#define FD_TDER1_VCXO_FREQ_R(reg) WBGEN2_GEN_READ(reg, 0, 32)
/* definitions for register: Test/Debug Register 1 */
/* definitions for field: Peltier PWM drive in reg: Test/Debug Register 1 */
#define FD_TDER2_PELT_DRIVE_MASK WBGEN2_GEN_MASK(0, 32)
#define FD_TDER2_PELT_DRIVE_SHIFT 0
#define FD_TDER2_PELT_DRIVE_W(value) WBGEN2_GEN_WRITE(value, 0, 32)
#define FD_TDER2_PELT_DRIVE_R(reg) WBGEN2_GEN_READ(reg, 0, 32)
/* definitions for register: Timestamp Buffer Debug Values Register */
/* definitions for register: Timestamp Buffer Advance Register */
/* definitions for field: Advance buffer readout in reg: Timestamp Buffer Advance Register */
#define FD_TSBR_ADVANCE_ADV WBGEN2_GEN_MASK(0, 1)
/* definitions for register: FMC Slot ID Register */
/* definitions for field: Slot ID in reg: FMC Slot ID Register */
#define FD_FMC_SLOT_ID_SLOT_ID_MASK WBGEN2_GEN_MASK(0, 4)
#define FD_FMC_SLOT_ID_SLOT_ID_SHIFT 0
#define FD_FMC_SLOT_ID_SLOT_ID_W(value) WBGEN2_GEN_WRITE(value, 0, 4)
#define FD_FMC_SLOT_ID_SLOT_ID_R(reg) WBGEN2_GEN_READ(reg, 0, 4)
/* definitions for register: I/O Delay Adjust Register */
/* definitions for field: Number of delay line taps. in reg: I/O Delay Adjust Register */
#define FD_IODELAY_ADJ_N_TAPS_MASK WBGEN2_GEN_MASK(0, 6)
#define FD_IODELAY_ADJ_N_TAPS_SHIFT 0
#define FD_IODELAY_ADJ_N_TAPS_W(value) WBGEN2_GEN_WRITE(value, 0, 6)
#define FD_IODELAY_ADJ_N_TAPS_R(reg) WBGEN2_GEN_READ(reg, 0, 6)
/* definitions for register: Interrupt disable register */
/* definitions for field: Timestamp Buffer interrupt. in reg: Interrupt disable register */
#define FD_EIC_IDR_TS_BUF_NOTEMPTY WBGEN2_GEN_MASK(0, 1)
/* definitions for field: DMTD SoftPLL interrupt in reg: Interrupt disable register */
#define FD_EIC_IDR_DMTD_SPLL WBGEN2_GEN_MASK(1, 1)
/* definitions for field: Sync Status Changed in reg: Interrupt disable register */
#define FD_EIC_IDR_SYNC_STATUS WBGEN2_GEN_MASK(2, 1)
/* definitions for register: Interrupt enable register */
/* definitions for field: Timestamp Buffer interrupt. in reg: Interrupt enable register */
#define FD_EIC_IER_TS_BUF_NOTEMPTY WBGEN2_GEN_MASK(0, 1)
/* definitions for field: DMTD SoftPLL interrupt in reg: Interrupt enable register */
#define FD_EIC_IER_DMTD_SPLL WBGEN2_GEN_MASK(1, 1)
/* definitions for field: Sync Status Changed in reg: Interrupt enable register */
#define FD_EIC_IER_SYNC_STATUS WBGEN2_GEN_MASK(2, 1)
/* definitions for register: Interrupt mask register */
/* definitions for field: Timestamp Buffer interrupt. in reg: Interrupt mask register */
#define FD_EIC_IMR_TS_BUF_NOTEMPTY WBGEN2_GEN_MASK(0, 1)
/* definitions for field: DMTD SoftPLL interrupt in reg: Interrupt mask register */
#define FD_EIC_IMR_DMTD_SPLL WBGEN2_GEN_MASK(1, 1)
/* definitions for field: Sync Status Changed in reg: Interrupt mask register */
#define FD_EIC_IMR_SYNC_STATUS WBGEN2_GEN_MASK(2, 1)
/* definitions for register: Interrupt status register */
/* definitions for field: Timestamp Buffer interrupt. in reg: Interrupt status register */
#define FD_EIC_ISR_TS_BUF_NOTEMPTY WBGEN2_GEN_MASK(0, 1)
/* definitions for field: DMTD SoftPLL interrupt in reg: Interrupt status register */
#define FD_EIC_ISR_DMTD_SPLL WBGEN2_GEN_MASK(1, 1)
/* definitions for field: Sync Status Changed in reg: Interrupt status register */
#define FD_EIC_ISR_SYNC_STATUS WBGEN2_GEN_MASK(2, 1)
/* [0x0]: REG Reset Register */
#define FD_REG_RSTR 0x00000000
/* [0x4]: REG ID Register */
#define FD_REG_IDR 0x00000004
/* [0x8]: REG Global Control Register */
#define FD_REG_GCR 0x00000008
/* [0xc]: REG Timing Control Register */
#define FD_REG_TCR 0x0000000c
/* [0x10]: REG Time Register - TAI seconds (MSB) */
#define FD_REG_TM_SECH 0x00000010
/* [0x14]: REG Time Register - TAI seconds (LSB) */
#define FD_REG_TM_SECL 0x00000014
/* [0x18]: REG Time Register - sub-second 125 MHz clock cycles */
#define FD_REG_TM_CYCLES 0x00000018
/* [0x1c]: REG Host-driven TDC Data Register. */
#define FD_REG_TDR 0x0000001c
/* [0x20]: REG Host-driven TDC Control/Status */
#define FD_REG_TDCSR 0x00000020
/* [0x24]: REG Calibration register */
#define FD_REG_CALR 0x00000024
/* [0x28]: REG DMTD Input Tag Register */
#define FD_REG_DMTR_IN 0x00000028
/* [0x2c]: REG DMTD Output Tag Register */
#define FD_REG_DMTR_OUT 0x0000002c
/* [0x30]: REG Acam Scaling Factor Register */
#define FD_REG_ADSFR 0x00000030
/* [0x34]: REG Acam Timestamp Merging Control Register */
#define FD_REG_ATMCR 0x00000034
/* [0x38]: REG Acam Start Offset Register */
#define FD_REG_ASOR 0x00000038
/* [0x3c]: REG Raw Input Events Counter Register */
#define FD_REG_IECRAW 0x0000003c
/* [0x40]: REG Tagged Input Events Counter Register */
#define FD_REG_IECTAG 0x00000040
/* [0x44]: REG Input Event Processing Delay Register */
#define FD_REG_IEPD 0x00000044
/* [0x48]: REG SPI Control Register */
#define FD_REG_SCR 0x00000048
/* [0x4c]: REG Reference Clock Rate Register */
#define FD_REG_RCRR 0x0000004c
/* [0x50]: REG Timestamp Buffer Control Register */
#define FD_REG_TSBCR 0x00000050
/* [0x54]: REG Timestamp Buffer Interrupt Register */
#define FD_REG_TSBIR 0x00000054
/* [0x58]: REG Timestamp Buffer Readout Seconds Register (MSB) */
#define FD_REG_TSBR_SECH 0x00000058
/* [0x5c]: REG Timestamp Buffer Readout Seconds Register (LSB) */
#define FD_REG_TSBR_SECL 0x0000005c
/* [0x60]: REG Timestamp Buffer Readout Cycles Register */
#define FD_REG_TSBR_CYCLES 0x00000060
/* [0x64]: REG Timestamp Buffer Readout Fine/Channel/Sequence ID Register */
#define FD_REG_TSBR_FID 0x00000064
/* [0x68]: REG I2C Bit-banged IO Register */
#define FD_REG_I2CR 0x00000068
/* [0x6c]: REG Test/Debug Register 1 */
#define FD_REG_TDER1 0x0000006c
/* [0x70]: REG Test/Debug Register 1 */
#define FD_REG_TDER2 0x00000070
/* [0x74]: REG Timestamp Buffer Debug Values Register */
#define FD_REG_TSBR_DEBUG 0x00000074
/* [0x78]: REG Timestamp Buffer Advance Register */
#define FD_REG_TSBR_ADVANCE 0x00000078
/* [0x7c]: REG FMC Slot ID Register */
#define FD_REG_FMC_SLOT_ID 0x0000007c
/* [0x80]: REG I/O Delay Adjust Register */
#define FD_REG_IODELAY_ADJ 0x00000080
/* [0xa0]: REG Interrupt disable register */
#define FD_REG_EIC_IDR 0x000000a0
/* [0xa4]: REG Interrupt enable register */
#define FD_REG_EIC_IER 0x000000a4
/* [0xa8]: REG Interrupt mask register */
#define FD_REG_EIC_IMR 0x000000a8
/* [0xac]: REG Interrupt status register */
#define FD_REG_EIC_ISR 0x000000ac
#endif
struct ad9516_reg {
int reg;
int val;
};
const struct ad9516_reg __9516_regs[] = {
{0x0000, 0x99}, /* Config SPI */
{0x0001, 0x00},
{0x0002, 0x10},
{0x0003, 0xC3},
{0x0004, 0x00},
/* PLL */
{0x0010, 0x7C}, /* PFD and charge pump */
{0x0011, 0x05}, /* R divider (1) */
{0x0012, 0x00}, /* R divider (2) */
{0x0013, 0x0C}, /* A counter */
{0x0014, 0x12}, /* B counter (1) */
{0x0015, 0x00}, /* B counter (2) */
{0x0016, 0x05}, /* PLL control (1) */
{0x0017, 0xb4}, /* PLL control (2) PLL_STATUS = Lock Detect */
{0x0018, 0x07}, /* PLL control (3) */
{0x0019, 0x00}, /* PLL control (4) */
{0x001A, 0x00}, /* PLL control (5) */
{0x001B, 0xE0}, /* PLL control (6) */
{0x001C, 0x02}, /* PLL control (7) */
{0x001D, 0x00}, /* PLL control (8) */
{0x001E, 0x00}, /* PLL control (9) */
{0x001F, 0x0E}, /* PLL readback */
/* Fine Delay */
{0x00A0, 0x01}, /* OUT6 Delay bypass */
{0x00A1, 0x00}, /* OUT6 Delay full-scale */
{0x00A2, 0x00}, /* OUT6 Delay fraction */
{0x00A3, 0x01}, /* OUT7 Delay bypass */
{0x00A4, 0x00}, /* OUT7 Delay full-scale */
{0x00A5, 0x00}, /* OUT7 Delay fraction */
{0x00A6, 0x01}, /* OUT8 Delay bypass */
{0x00A7, 0x00}, /* OUT8 Delay full-scale */
{0x00A8, 0x00}, /* OUT8 Delay fraction */
{0x00A9, 0x01}, /* OUT9 Delay bypass */
{0x00AA, 0x00}, /* OUT9 Delay full-scale */
{0x00AB, 0x00}, /* OUT9 Delay fraction */
/* LVPECL */
{0x00F0, 0x08}, /* OUT0 */
{0x00F1, 0x08}, /* OUT1 */
{0x00F2, 0x08}, /* OUT2 */
{0x00F3, 0x18}, /* OUT3, inverted */
{0x00F4, 0x00}, /* OUT4 */
{0x00F5, 0x08}, /* OUT5 */
/* LVDS/CMOS */
{0x0140, 0x5A}, /* OUT6 */
{0x0141, 0x5A}, /* OUT7 */
{0x0142, 0x5B}, /* OUT8 */
{0x0143, 0x42}, /* OUT9 */
/* LVPECL Channel divider */
{0x0190, 0x00}, /* Divider 0 (1) */
{0x0191, 0x80}, /* Divider 0 (2) */
{0x0192, 0x00}, /* Divider 0 (3) */
{0x0193, 0x00}, /* Divider 1 (1) */
{0x0194, 0x80}, /* Divider 1 (2) */
{0x0195, 0x00}, /* Divider 1 (3) */
{0x0196, 0xFF}, /* Divider 2 (1) */
{0x0197, 0x00}, /* Divider 2 (2) */
{0x0198, 0x00}, /* Divider 2 (3) */
/* LVDS/CMOS Channel divider */
{0x0199, 0x33}, /* Divider 3 (1) */
{0x019A, 0x00}, /* Divider 3 (2) */
{0x019B, 0x11}, /* Divider 3 (3) */
{0x019C, 0x20}, /* Divider 3 (4) */
{0x019D, 0x00}, /* Divider 3 (5) */
{0x019E, 0x00}, /* Divider 4 (1) */
{0x019F, 0x00}, /* Divider 4 (2) */
{0x01A0, 0x11}, /* Divider 4 (3) */
{0x01A1, 0x20}, /* Divider 4 (4) */
{0x01A2, 0x00}, /* Divider 4 (5) */
{0x01A3, 0x00},
/* VCO Divider and CLK Input */
{0x01E0, 0x04}, /* VCO divider VCODIV = 6 */
{0x01E1, 0x02}, /* Input Clock */
/* System */
{0x0230, 0x00}, /* Power down and sync */
{0x0231, 0x00},
/* Update All registers */
{0x0232, 0x00}, /* Update All registers */
};
/*
* Access to 1w thermometer
*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2 as published by the Free Software Foundation or, at your
* option, any later version.
*/
#include <linux/jiffies.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/delay.h>
#include "fine-delay.h"
#include "hw/fd_main_regs.h"
#define R_CSR 0x0
#define R_CDR 0x4
#define CSR_DAT_MSK (1<<0)
#define CSR_RST_MSK (1<<1)
#define CSR_OVD_MSK (1<<2)
#define CSR_CYC_MSK (1<<3)
#define CSR_PWR_MSK (1<<4)
#define CSR_IRQ_MSK (1<<6)
#define CSR_IEN_MSK (1<<7)
#define CSR_SEL_OFS 8
#define CSR_SEL_MSK (0xF<<8)
#define CSR_POWER_OFS 16
#define CSR_POWER_MSK (0xFFFF<<16)
#define CDR_NOR_MSK (0xFFFF<<0)
#define CDR_OVD_OFS 16
#define CDR_OVD_MSK (0xFFFF<<16)
#define CLK_DIV_NOR (624/2)
#define CLK_DIV_OVD (124/2)
#define CMD_ROM_SEARCH 0xF0
#define CMD_ROM_READ 0x33
#define CMD_ROM_MATCH 0x55
#define CMD_ROM_SKIP 0xCC
#define CMD_ROM_ALARM_SEARCH 0xEC
#define CMD_CONVERT_TEMP 0x44
#define CMD_WRITE_SCRATCHPAD 0x4E
#define CMD_READ_SCRATCHPAD 0xBE
#define CMD_COPY_SCRATCHPAD 0x48
#define CMD_RECALL_EEPROM 0xB8
#define CMD_READ_POWER_SUPPLY 0xB4
#define FD_OW_PORT 0 /* what is this slow? */
static void ow_writel(struct fd_dev *fd, uint32_t val, unsigned long reg)
{
fd_iowrite(fd, val, fd->fd_owregs_base + reg);
}
static uint32_t ow_readl(struct fd_dev *fd, unsigned long reg)
{
return fd_ioread(fd, fd->fd_owregs_base + reg);
}
static int ow_reset(struct fd_dev *fd, int port)
{
uint32_t reg, data;
data = ((port << CSR_SEL_OFS) & CSR_SEL_MSK)
| CSR_CYC_MSK | CSR_RST_MSK;
ow_writel(fd, data, R_CSR);
while(ow_readl(fd, R_CSR) & CSR_CYC_MSK)
udelay(10);
reg = ow_readl(fd, R_CSR);
return ~reg & CSR_DAT_MSK;
}
static int slot(struct fd_dev *fd, int port, int bit)
{
uint32_t reg, data;
data = ((port<<CSR_SEL_OFS) & CSR_SEL_MSK)
| CSR_CYC_MSK | (bit & CSR_DAT_MSK);
ow_writel(fd, data, R_CSR);
while(ow_readl(fd, R_CSR) & CSR_CYC_MSK)
udelay(10);
reg = ow_readl(fd, R_CSR);
return reg & CSR_DAT_MSK;
}
static int read_bit(struct fd_dev *fd, int port)
{
return slot(fd, port, 0x1);
}
static int write_bit(struct fd_dev *fd, int port, int bit)
{
return slot(fd, port, bit);
}
static int ow_read_byte(struct fd_dev *fd, int port)
{
int byte = 0, i;
for(i = 0; i < 8; i++)
byte |= (read_bit(fd, port) << i);
return byte;
}
static int ow_write_byte(struct fd_dev *fd, int port, int byte)
{
int data = 0;
int i;
for (i = 0; i < 8; i++){
data |= write_bit(fd, port, (byte & 0x1)) << i;
byte >>= 1;
}
return 0; /* success */
}
static int ow_write_block(struct fd_dev *fd, int port, uint8_t *block, int len)
{
int i;
for(i = 0; i < len; i++)
ow_write_byte(fd, port, block[i]);
return 0;
}
static int ow_read_block(struct fd_dev *fd, int port, uint8_t *block, int len)
{
int i;
for(i = 0; i < len; i++)
block[i] = ow_read_byte(fd, port);
return 0;
}
static int ds18x_read_serial(struct fd_dev *fd)
{
if(!ow_reset(fd, 0)) {
dev_err(&fd->pdev->dev, "Failure in resetting one-wire channel\n");
return -EIO;
}
ow_write_byte(fd, FD_OW_PORT, CMD_ROM_READ);
return ow_read_block(fd, FD_OW_PORT, fd->ds18_id, 8);
}
static int ds18x_access(struct fd_dev *fd)
{
if(!ow_reset(fd, 0))
goto out;
if (0) {
/* select the rom among several of them */
if (ow_write_byte(fd, FD_OW_PORT, CMD_ROM_MATCH) < 0)
goto out;
return ow_write_block(fd, FD_OW_PORT, fd->ds18_id, 8);
} else {
/* we have one only, so skip rom */
return ow_write_byte(fd, FD_OW_PORT, CMD_ROM_SKIP);
}
out:
dev_err(&fd->pdev->dev, "Failure in one-wire communication\n");
return -EIO;
}
static void __temp_command_and_next_t(struct fd_dev *fd, int cfg_reg)
{
int ms;
ds18x_access(fd);
ow_write_byte(fd, FD_OW_PORT, CMD_CONVERT_TEMP);
/* The conversion takes some time, so mark when will it be ready */
ms = 94 * ( 1 << (cfg_reg >> 5));
fd->next_t = jiffies + msecs_to_jiffies(ms);
}
int fd_read_temp(struct fd_dev *fd, int verbose)
{
int i, temp;
unsigned long j;
uint8_t data[9];
struct device *dev = &fd->pdev->dev;
/* If first conversion, ask for it first */
if (fd->next_t == 0)
__temp_command_and_next_t(fd, 0x7f /* we ignore: max time */);
/* Wait for it to be ready: (FIXME: we need a time policy here) */
j = jiffies;
if (time_before(j, fd->next_t)) {
/* If we cannot sleep, return the previous value */
if (in_atomic())
return fd->temp;
msleep(jiffies_to_msecs(fd->next_t - j));
}
ds18x_access(fd);
ow_write_byte(fd, FD_OW_PORT, CMD_READ_SCRATCHPAD);
ow_read_block(fd, FD_OW_PORT, data, 9);
if (verbose > 1) {
dev_info(dev, "%s: Scratchpad: ", __func__);
for (i = 0; i < 9; i++)
printk("%02x%c", data[i], i == 8 ? '\n' : ':');
}
temp = ((int)data[1] << 8) | ((int)data[0]);
if(temp & 0x1000)
temp = -0x10000 + temp;
fd->temp = temp;
fd->temp_ready = 1;
if (verbose) {
dev_info(dev, "%s: Temperature 0x%x (%i bits: %i.%03i)\n", __func__,
temp, 9 + (data[4] >> 5),
temp / 16, (temp & 0xf) * 1000 / 16);
}
__temp_command_and_next_t(fd, data[4]); /* start next conversion */
return temp;
}
int fd_onewire_init(struct fd_dev *fd)
{
int i;
ow_writel(fd, ((CLK_DIV_NOR & CDR_NOR_MSK)
| (( CLK_DIV_OVD << CDR_OVD_OFS) & CDR_OVD_MSK)),
R_CDR);
if(ds18x_read_serial(fd) < 0)
return -EIO;
if (fd->verbose) {
dev_info(&fd->pdev->dev, "%s: Found DS18xx sensor: ", __func__);
for (i = 0; i < 8; i++)
printk("%02x%c", fd->ds18_id[i], i == 7 ? '\n' : ':');
}
/* read the temperature once, to ensure it works, and print it */
fd_read_temp(fd, fd->verbose);
return 0;
}
void fd_onewire_exit(struct fd_dev *fd)
{
/* Nothing to do */
}
/*
* PLL access (AD9516) for fine-delay card
*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2 as published by the Free Software Foundation or, at your
* option, any later version.
*/
#include <linux/kernel.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/jiffies.h>
#include "fine-delay.h"
#include "hw/pll_config.h" /* the table to be written */
static int pll_writel(struct fd_dev *fd, int val, int reg)
{
return fd_spi_xfer(fd, FD_CS_PLL, 24, (reg << 8) | val, NULL);
}
static int pll_readl(struct fd_dev *fd, int reg)
{
uint32_t ret;
int err;
err = fd_spi_xfer(fd, FD_CS_PLL, 24, (reg << 8) | (1 << 23), &ret);
if (err < 0)
return err;
return ret & 0xff;
}
int fd_pll_init(struct fd_dev *fd)
{
int i;
unsigned long j;
const struct ad9516_reg *r;
struct device *dev = &fd->pdev->dev;
if (pll_writel(fd, 0x99, 0x000) < 0)
goto out;
if (pll_writel(fd, 0x01, 0x232) < 0)
goto out;
i = pll_readl(fd, 0x003);
if (i < 0)
goto out;
if (i != 0xc3) {
dev_err(dev, "Error in PLL communication\n");
dev_err(dev, " (got 0x%x, expected 0xc3)\n", i);
return -EIO;
}
/* Write the magic config */
for (i = 0, r = __9516_regs; i < ARRAY_SIZE(__9516_regs); i++, r++) {
if (pll_writel(fd, r->val, r->reg) < 0) {
dev_err(dev, "Error in configuring PLL (step %i)\n", i);
return -EIO;
}
}
if (pll_writel(fd, 0x01, 0x232) < 0)
goto out;
/* Wait for it to lock */
j = jiffies + HZ / 2;
while (jiffies < j) {
i = pll_readl(fd, 0x1f);
if (i < 0)
return -EIO;
if (i & 1)
break;
msleep(1);
}
if (!(i & 1))
return -ETIMEDOUT;
/*
* Synchronize the phase of all clock outputs
* (this is critical for the accuracy!)
*/
if (pll_writel(fd, 0x01, 0x230) < 0)
goto out;
if (pll_writel(fd, 0x01, 0x232) < 0)
goto out;
if (pll_writel(fd, 0x00, 0x230) < 0)
goto out;
if (pll_writel(fd, 0x01, 0x232) < 0)
goto out;
return 0;
out:
dev_err(dev, "Error in SPI communication\n");
return -EIO;
}
void fd_pll_exit(struct fd_dev *fd)
{
/* nothing to do */
}
/*
* SPI access to fine-delay internals
*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2 as published by the Free Software Foundation or, at your
* option, any later version.
*/
#include <linux/jiffies.h>
#include <linux/io.h>
#include <linux/delay.h>
#include "fine-delay.h"
#include "hw/fd_main_regs.h"
int fd_spi_xfer(struct fd_dev *fd, int ss, int num_bits,
uint32_t in, uint32_t *out)
{
uint32_t scr = 0, r;
unsigned long j = jiffies + HZ;
scr = FD_SCR_DATA_W(in)| FD_SCR_CPOL;
if(ss == FD_CS_PLL)
scr |= FD_SCR_SEL_PLL;
else if(ss == FD_CS_GPIO)
scr |= FD_SCR_SEL_GPIO;
fd_writel(fd, scr, FD_REG_SCR);
fd_writel(fd, scr | FD_SCR_START, FD_REG_SCR);
while (!(fd_readl(fd, FD_REG_SCR) & FD_SCR_READY))
if (jiffies > j)
break;
if (!(fd_readl(fd, FD_REG_SCR) & FD_SCR_READY))
return -EIO;
scr = fd_readl(fd, FD_REG_SCR);
r = FD_SCR_DATA_R(scr);
if(out) *out=r;
udelay(100); /* FIXME: check */
return 0;
}
int fd_spi_init(struct fd_dev *fd)
{
/* write default to DAC for VCXO */
fd_spi_xfer(fd, FD_CS_DAC, 24, fd->calib.vcxo_default_tune & 0xffff,
NULL);
return 0;
}
void fd_spi_exit(struct fd_dev *fd)
{
/* nothing to do */
}
/*
* SPI access to fine-delay internals
*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2 as published by the Free Software Foundation or, at your
* option, any later version.
*/
#include <linux/io.h>
#include <linux/time.h>
#include <linux/spinlock.h>
#include "fine-delay.h"
#include "hw/fd_main_regs.h"
/* If fd_time is not null, use it. if ts is not null, use it, else current */
int fd_time_set(struct fd_dev *fd, struct fd_time *t, struct timespec *ts)
{
uint32_t tcr, gcr;
unsigned long flags;
struct timespec localts;
spin_lock_irqsave(&fd->lock, flags);
gcr = fd_readl(fd, FD_REG_GCR);
fd_writel(fd, 0, FD_REG_GCR); /* zero the GCR while setting time */
if (t) {
fd_writel(fd, t->utc >> 32, FD_REG_TM_SECH);
fd_writel(fd, t->utc & 0xffffffff, FD_REG_TM_SECL);
fd_writel(fd, t->coarse, FD_REG_TM_CYCLES);
} else {
if (!ts) {
/* no caller-provided time: use Linux timer */
ts = &localts;
getnstimeofday(ts);
}
fd_writel(fd, GET_HI32(ts->tv_sec), FD_REG_TM_SECH);
fd_writel(fd, (int32_t)ts->tv_sec, FD_REG_TM_SECL);
fd_writel(fd, ts->tv_nsec >> 3, FD_REG_TM_CYCLES);
}
tcr = fd_readl(fd, FD_REG_TCR);
fd_writel(fd, tcr | FD_TCR_SET_TIME, FD_REG_TCR);
fd_writel(fd, gcr, FD_REG_GCR); /* Restore GCR */
spin_unlock_irqrestore(&fd->lock, flags);
return 0;
}
/* If fd_time is not null, use it. Otherwise use ts */
int fd_time_get(struct fd_dev *fd, struct fd_time *t, struct timespec *ts)
{
uint32_t tcr, h, l, c;
unsigned long flags;
spin_lock_irqsave(&fd->lock, flags);
tcr = fd_readl(fd, FD_REG_TCR);
fd_writel(fd, tcr | FD_TCR_CAP_TIME, FD_REG_TCR);
h = fd_readl(fd, FD_REG_TM_SECH);
l = fd_readl(fd, FD_REG_TM_SECL);
c = fd_readl(fd, FD_REG_TM_CYCLES);
spin_unlock_irqrestore(&fd->lock, flags);
if (t) {
t->utc = ((uint64_t)h << 32) | l;
t->coarse = c;
}
if (ts) {
ts->tv_sec = ((uint64_t)h << 32) | l;
ts->tv_nsec = c * 8;
}
return 0;
}
int fd_time_init(struct fd_dev *fd)
{
struct timespec ts = {0,0};
/* Set the time to zero, so internal stuff resyncs */
return fd_time_set(fd, NULL, &ts);
}
void fd_time_exit(struct fd_dev *fd)
{
/* nothing to do */
}
*.a
.depend
*.so*
\ No newline at end of file
# This is not a kbuild Makefile. It is a plain Makefile so it can be copied
# If it exists includes Makefile.specific. In this Makefile, you should put
# specific Makefile code that you want to run before this. For example,
# build a particular environment.
-include Makefile.specific
# include parent_common.mk for buildsystem's defines
REPO_PARENT=../..
-include $(REPO_PARENT)/parent_common.mk
ZIO ?= ../zio
ZIO_ABS ?= $(abspath $(ZIO) )
VERSION := $(shell git describe --tags --abbrev=0 | tr -d 'v')
SO_VERSION_XYZ := $(shell echo $(VERSION) | grep -o -E "[0-9]+\.[0-9]+\.[0-9]")
SO_VERSION_X := $(shell echo $(SO_VERSION_XYZ) | cut -d "." -f 1)
LIB = libfdelay.a
LIBS = libfdelay.so
LIBS_XYZ = $(LIBS).$(SO_VERSION_XYZ)
LOBJ := fdelay-init.o
LOBJ += fdelay-time.o
LOBJ += fdelay-tdc.o
LOBJ += fdelay-output.o
GIT_VERSION := $(shell git describe --dirty --long --tags)
ZIO_GIT_VERSION := $(shell cd $(ZIO_ABS); git describe --dirty --long --tags)
CFLAGS = -Wall -ggdb -O2 -I../kernel -I$(ZIO_ABS)/include
CFLAGS += -fPIC
CFLAGS += -DGIT_VERSION="\"$(GIT_VERSION)\""
CFLAGS += -DZIO_GIT_VERSION="\"$(ZIO_GIT_VERSION)\""
CFLAGS += $(EXTRACFLAGS)
LDFLAGS = -L. -lfdelay
DESTDIR ?= /usr/local
CPPCHECK ?= cppcheck
modules all: lib
lib: $(LIB) $(LIBS_XYZ)
%: %.c $(LIB)
$(CC) $(CFLAGS) $*.c $(LDFLAGS) -o $@
$(LIB): $(LOBJ)
$(AR) r $@ $^
$(LIBS_XYZ): $(LIB)
$(CC) -shared -o $@ -Wl,--whole-archive,-soname,$@ $^ -Wl,--no-whole-archive
clean:
rm -f $(LIB) $(LIBS_XYZ) .depend *.o *~
.depend: Makefile $(wildcard *.c *.h ../*.h)
$(CC) $(CFLAGS) -M $(LOBJ:.o=.c) -o $@
install:
install -d $(DESTDIR)/lib
install -d $(DESTDIR)/include/fmc-fdelay
install -m 644 -D $(LIB) $(DESTDIR)/lib
install -m 0755 $(LIBS_XYZ) $(DESTDIR)/lib
install -m 644 -D fdelay-lib.h $(DESTDIR)/include/fmc-fdelay
install -m 644 -D ../kernel/fine-delay.h $(DESTDIR)/include/fmc-fdelay
ln -sf $(LIBS_XYZ) $(DESTDIR)/lib/$(LIBS).$(SO_VERSION_X)
ln -sf $(LIBS).$(SO_VERSION_X) $(DESTDIR)/lib/$(LIBS)
modules_install:
cppcheck:
$(CPPCHECK) -q -I. -I../kernel -I $(ZIO_ABS)/include --suppress=missingIncludeSystem --enable=warning,style,information,missingInclude *.c *.h
-include .depend
.PHONY=cppcheck
# SPDX-License-Identifier: CC0-1.0
#
# SPDX-FileCopyrightText: 2020 CERN
*.pyc
MANIFEST
# SPDX-License-Identifier: CC0-1.0
#
# SPDX-FileCopyrightText: 2020 CERN
-include Makefile.specific
all:
clean:
install:
python setup.py install
.PHONY: all clean install
"""
@package docstring
@author: Federico Vaga <federico.vaga@cern.ch>
SPDX-License-Identifier: LGPL-3.0-or-later
SPDX-FileCopyrightText: 2020 CERN (home.cern)
"""
import atexit
import select
import errno
import time
import os
from ctypes import CDLL,\
c_uint64, c_uint32, c_int, c_void_p, c_char_p, c_float,\
pointer, POINTER, set_errno, get_errno,\
Structure
class FmcFineDelayTime(Structure):
_fields_ = [
("seconds", c_uint64),
("coarse", c_uint32),
("frac", c_uint32),
("seq_id", c_uint32),
("channel", c_uint32),
]
@classmethod
def from_pico(cls, pico):
tim = cls()
pico_u64 = c_uint64(pico)
libfdelay.fdelay_pico_to_time(pointer(pico_u64), pointer(tim))
return tim
def __str__(self):
return "seconds: {:d}, coarse: {:d}, frac: {:d}, seq_id: {:d}, channel: {:d}".format(self.seconds, self.coarse, self.frac, self.seq_id, self.channel)
def __float__(self):
ts = self.seconds
ts = ts + (self.coarse / 1000000000.0 * 8)
ts = ts + ((self.frac * 7.999) / 4095) / 1000000000
return ts
def __add__(self, other):
res = FmcFineDelayTime()
res.seconds = 0
res.coarse = 0
res.frac = 0
res.seq_id = -1
res.channel = -1
res.frac = self.frac + other.frac
if res.frac >= 4096:
res.frac -= 4096
res.coarse += 1
res.coarse += self.coarse + other.coarse
if res.coarse >= 125000000:
res.coarse -= 125000000
res.seconds += 1
res.seconds += self.seconds + other.seconds
return res
def __sub__(self, other):
res = FmcFineDelayTime()
res.seq_id = -1
res.channel = -1
frac = self.frac - other.frac
if frac < 0:
res.frac = frac + 4096
carry = 1
else:
res.frac = frac
carry = 0
coarse = self.coarse - other.coarse - carry
if coarse < 0:
res.coarse = coarse + 125000000
carry = 1
else:
res.coarse = coarse
carry = 0
res.seconds = self.seconds - other.seconds - carry
return res
def __gt__(self, other):
if self.seconds > other.seconds:
return True
elif self.seconds < other.seconds:
return False
if self.coarse > other.coarse:
return True
elif self.coarse < other.coarse:
return False
if self.frac > other.frac:
return True
elif self.frac < other.frac:
return False
return False
class FmcFineDelayPulse(Structure):
_fields_ = [
("mode", c_int),
("rep", c_int),
("start", FmcFineDelayTime),
("end", FmcFineDelayTime),
("loop", FmcFineDelayTime),
]
class FmcFineDelayPulsePs(Structure):
_fields_ = [
("mode", c_int),
("rep", c_int),
("start", FmcFineDelayTime),
("length", c_uint64),
("period", c_uint64),
]
def errcheck_pointer(ret, func, args):
"""Generic error handler for functions returning pointers"""
lib = CDLL("libfdelay.so", use_errno=True)
if ret is None:
raise OSError(get_errno(),
lib.fdelay_strerror(get_errno()),
"")
else:
return ret
def errcheck_int(ret, func, args):
"""Generic error checker for functions returning 0 as success
and -1 as error"""
lib = CDLL("libfdelay.so", use_errno=True)
if ret < 0:
raise OSError(get_errno(),
lib.fdelay_strerror(get_errno()),
"")
else:
return ret
def libfdelay_create():
lib = CDLL("libfdelay.so", use_errno=True)
lib.fdelay_init.argtypes = []
lib.fdelay_init.restype = c_int
lib.fdelay_init.errcheck = errcheck_int
lib.fdelay_strerror.argtypes = [c_int]
lib.fdelay_strerror.restype = c_char_p
lib.fdelay_open.argtypes = [c_int]
lib.fdelay_open.restype = c_void_p
lib.fdelay_open.errcheck = errcheck_pointer
lib.fdelay_close.argtypes = [c_void_p]
lib.fdelay_close.restype = c_int
lib.fdelay_close.errcheck = errcheck_int
# Device
lib.fdelay_read_temperature.argtypes = [c_void_p]
lib.fdelay_read_temperature.restype = c_float
lib.fdelay_wr_mode.argtypes = [c_void_p, c_int]
lib.fdelay_wr_mode.restype = c_int
lib.fdelay_wr_mode.errcheck = errcheck_int
lib.fdelay_check_wr_mode.argtypes = [c_void_p]
lib.fdelay_check_wr_mode.restype = c_int
lib.fdelay_set_time.argtypes = [c_void_p, POINTER(FmcFineDelayTime)]
lib.fdelay_set_time.restype = c_int
lib.fdelay_set_time.errcheck = errcheck_int
lib.fdelay_get_time.argtypes = [c_void_p, POINTER(FmcFineDelayTime)]
lib.fdelay_get_time.restype = c_int
lib.fdelay_get_time.errcheck = errcheck_int
# Channel
lib.fdelay_config_pulse.argtypes = [c_void_p, c_int,
POINTER(FmcFineDelayPulse)]
lib.fdelay_config_pulse.restype = c_int
lib.fdelay_config_pulse.errcheck = errcheck_int
lib.fdelay_config_pulse_ps.argtypes = [c_void_p, c_int,
POINTER(FmcFineDelayPulsePs)]
lib.fdelay_config_pulse_ps.restype = c_int
lib.fdelay_config_pulse_ps.errcheck = errcheck_int
lib.fdelay_get_config_pulse.argtypes = [c_void_p, c_int,
POINTER(FmcFineDelayPulse)]
lib.fdelay_get_config_pulse.restype = c_int
lib.fdelay_get_config_pulse.errcheck = errcheck_int
lib.fdelay_get_config_pulse_ps.argtypes = [c_void_p, c_int,
POINTER(FmcFineDelayPulsePs)]
lib.fdelay_get_config_pulse_ps.restype = c_int
lib.fdelay_get_config_pulse_ps.errcheck = errcheck_int
lib.fdelay_has_triggered.argtypes = [c_void_p, c_int]
lib.fdelay_has_triggered.restype = c_int
lib.fdelay_has_triggered.errcheck = errcheck_int
# TDC
lib.fdelay_set_config_tdc.argtypes = [c_void_p, c_int]
lib.fdelay_set_config_tdc.restype = c_int
lib.fdelay_set_config_tdc.errcheck = errcheck_int
lib.fdelay_get_config_tdc.argtypes = [c_void_p]
lib.fdelay_get_config_tdc.restype = c_int
lib.fdelay_get_config_tdc.errcheck = errcheck_int
lib.fdelay_fileno_tdc.argtypes = [c_void_p]
lib.fdelay_fileno_tdc.restype = c_int
lib.fdelay_fileno_tdc.errcheck = errcheck_int
lib.fdelay_read.argtypes = [c_void_p, POINTER(FmcFineDelayTime),
c_int, c_int]
lib.fdelay_read.restype = c_int
lib.fdelay_read.errcheck = errcheck_int
lib.fdelay_fread.argtypes = [c_void_p, POINTER(FmcFineDelayTime), c_int]
lib.fdelay_fread.restype = c_int
lib.fdelay_fread.errcheck = errcheck_int
# util
lib.fdelay_pico_to_time.argtypes = [POINTER(c_uint64),
POINTER(FmcFineDelayTime)]
lib.fdelay_time_to_pico.argtypes = [POINTER(FmcFineDelayTime),
POINTER(c_uint64)]
return lib
libfdelay = libfdelay_create()
def libfdelay_load():
libfdelay.fdelay_init()
def libfdelay_unload():
libfdelay.fdelay_exit()
atexit.register(libfdelay_unload)
class FmcFineDelay(object):
"""
It is a Python class that represent an FMC Fine Delay device
:param devid: FMC Fine Delay device identifier
:ivar device_id: device ID associated with the instance
:ivar tkn: device token to be used with the libfmcfd library
"""
CHANNEL_NUMBER = 4
class FmcFineDelayChannel(object):
OUT_MODE_DISABLED = 0
OUT_MODE_DELAY = 1
OUT_MODE_PULSE = 2
OUT_PULSE_MAX_COUNT = 0xFFFF
OUT_PULSE_MIN_WIDTH_PS = 50000
OUT_PULSE_MIN_PERIOD_PS = 100000
OUT_PULSE_MAX_WIDTH_PS = 1000000000000
OUT_PULSE_MAX_PERIOD_PS = 2000000000000
OUT_PULSE_DELAY_MIN_WIDTH_PS = 250000
OUT_PULSE_DELAY_MIN_PERIOD_PS = 500000
OUT_PULSE_DELAY_MAX_WIDTH_PS = 1000000000000
OUT_PULSE_DELAY_MAX_PERIOD_PS = 2000000000000
def __init__(self, dev, channel):
self.dev = dev
self.tkn = self.dev.tkn
self.idx = channel
@property
def triggered(self):
return libfdelay.fdelay_has_triggered(self.tkn, self.idx) == 1
@property
def disabled(self):
mode = self.pulse_config_raw.mode & 0x7F
return mode == self.OUT_MODE_DISABLED
def disable(self):
cfg = FmcFineDelayPulse()
cfg.mode = self.OUT_MODE_DISABLED
cfg.rep = 1
cfg.end.seconds = 0
cfg.start.coarse = 75
cfg.start.frac = 0
cfg.end.seconds = 0
cfg.end.coarse = 125
cfg.end.frac = 0
cfg.loop.seconds = 1
cfg.loop.coarse = 0
cfg.loop.frac = 0
self.pulse_config_raw = cfg
def pulse_delay(self, delay, width, period, count):
if count > self.OUT_PULSE_MAX_COUNT:
raise OSError(errno.EINVAL,
"'count' can be maximum {:d}".format(self.OUT_PULSE_MAX_COUNT))
if width < self.OUT_PULSE_DELAY_MIN_WIDTH_PS or \
width > self.OUT_PULSE_DELAY_MAX_WIDTH_PS:
raise OSError(errno.EINVAL,
"'width' must be in range [{:d}, {:d}]ps".format(self.OUT_PULSE_DELAY_MIN_WIDTH_PS,
self.OUT_PULSE_DELAY_MAX_WIDTH_PS))
if period < self.OUT_PULSE_DELAY_MIN_PERIOD_PS or \
period > self.OUT_PULSE_DELAY_MAX_PERIOD_PS:
raise OSError(errno.EINVAL,
"'period' must be in range [{:d}, {:d}]ps".format(self.OUT_PULSE_DELAY_MIN_PERIOD_PS,
self.OUT_PULSE_DELAY_MAX_PERIOD_PS))
start = FmcFineDelayTime()
delay_u64 = c_uint64(delay)
libfdelay.fdelay_pico_to_time(pointer(delay_u64),
pointer(start))
cfg = FmcFineDelayPulsePs()
cfg.mode = self.OUT_MODE_DELAY
cfg.rep = count
cfg.start = start
cfg.length = width
cfg.period = period
self.pulse_config_ps_raw = cfg
def pulse_generate(self, start, width, period, count):
if count > self.OUT_PULSE_MAX_COUNT:
raise OSError(errno.EINVAL,
"'count' can be maximum {:d}".format(self.OUT_PULSE_MAX_COUNT))
if width < self.OUT_PULSE_MIN_WIDTH_PS or \
width > self.OUT_PULSE_MAX_WIDTH_PS:
raise OSError(errno.EINVAL,
"'width' must be in range [{:d}, {:d}]ps".format(self.OUT_PULSE_MIN_WIDTH_PS,
self.OUT_PULSE_MAX_WIDTH_PS))
if period < self.OUT_PULSE_MIN_PERIOD_PS or \
period > self.OUT_PULSE_MAX_PERIOD_PS:
raise OSError(errno.EINVAL,
"'period' must be in range [{:d}, {:d}]ps".format(self.OUT_PULSE_MIN_PERIOD_PS,
self.OUT_PULSE_MAX_PERIOD_PS))
p = c_uint64(period)
cfg = FmcFineDelayPulse()
# cfg = FmcFineDelayPulsePs()
cfg.mode = self.OUT_MODE_PULSE
cfg.rep = count
cfg.start = start
libfdelay.fdelay_pico_to_time(pointer(c_uint64(width)),
pointer(cfg.end))
cfg.end += start
# cfg.length = width
libfdelay.fdelay_pico_to_time(pointer(c_uint64(period)),
pointer(cfg.loop))
# cfg.period = period
self.pulse_config_raw = cfg
# self.pulse_config_ps_raw = cfg
@property
def status(self):
return self.pulse_config_ps_raw
@property
def pulse_config_raw(self):
cfg = FmcFineDelayPulse()
libfdelay.fdelay_get_config_pulse(self.tkn, self.idx, pointer(cfg))
return cfg
@pulse_config_raw.setter
def pulse_config_raw(self, cfg):
libfdelay.fdelay_config_pulse(self.tkn, self.idx, pointer(cfg))
@property
def pulse_config_ps_raw(self):
cfg = FmcFineDelayPulsePs()
libfdelay.fdelay_get_config_pulse_ps(self.tkn, self.idx,
pointer(cfg))
return cfg
@pulse_config_ps_raw.setter
def pulse_config_ps_raw(self, cfg):
libfdelay.fdelay_config_pulse_ps(self.tkn, self.idx, pointer(cfg))
class FmcFineDelayTDC(object):
TDC_FLAG_INPUT_DISABLE = 0x1
TDC_FLAG_TSTAMP_DISABLE = 0x2
TDC_FLAG_TERM_ENABLE = 0x4
TDC_FLAG_MASK = TDC_FLAG_INPUT_DISABLE |\
TDC_FLAG_TSTAMP_DISABLE |\
TDC_FLAG_TERM_ENABLE
def __init__(self, tkn):
self.tkn = tkn
self.poll_desc = select.poll()
self.poll_desc.register(self.fileno, select.POLLIN)
@property
def enable_input(self):
return not bool(self.__config_get() & self.TDC_FLAG_INPUT_DISABLE)
@enable_input.setter
def enable_input(self, val):
old = self.__config_get()
if val:
old &= ~self.TDC_FLAG_INPUT_DISABLE
else:
old |= self.TDC_FLAG_INPUT_DISABLE
self.__config_set(old)
@property
def enable_tstamp(self):
return not bool(self.__config_get() & self.TDC_FLAG_TSTAMP_DISABLE)
@enable_tstamp.setter
def enable_tstamp(self, val):
old = self.__config_get()
if val:
old &= ~self.TDC_FLAG_TSTAMP_DISABLE
else:
old |= self.TDC_FLAG_TSTAMP_DISABLE
self.__config_set(old)
@property
def termination(self):
return bool(self.__config_get() & self.TDC_FLAG_TERM_ENABLE)
@termination.setter
def termination(self, val):
old = self.__config_get()
if val:
old |= self.TDC_FLAG_TERM_ENABLE
else:
old &= ~self.TDC_FLAG_TERM_ENABLE
self.__config_set(old)
@property
def fileno(self):
return libfdelay.fdelay_fileno_tdc(self.tkn)
def __config_get(self):
return libfdelay.fdelay_get_config_tdc(self.tkn)
def __config_set(self, flags):
return libfdelay.fdelay_set_config_tdc(self.tkn, flags)
def poll(self, timeout=10):
return self.poll_desc.poll(timeout)
def read(self, n=1, flags=0):
ts = (FmcFineDelayTime * n)()
ret = libfdelay.fdelay_read(self.tkn, ts, n, flags)
return list(ts)[:ret]
def fread(self, n=1, flags=0):
ts = (FmcFineDelayTime * n)()
ret = libfdelay.fdelay_fread(self.tkn, ts, n, flags)
return list(ts)[:ret]
def flush(self):
while True:
try:
if len(self.read(1024, os.O_NONBLOCK)) == 0:
break
except OSError:
break
def __init__(self, devid):
if devid is None:
raise Exception("Invalid device ID")
self.device_id = devid
libfdelay.fdelay_init()
set_errno(0)
self.tkn = libfdelay.fdelay_open(self.device_id)
self.chan = []
for i in range(self.CHANNEL_NUMBER):
self.chan.append(self.FmcFineDelayChannel(self, i))
self.tdc = self.FmcFineDelayTDC(self.tkn)
def __del__(self):
if hasattr(self, 'tkn'):
libfdelay.fdelay_close(self.tkn)
libfdelay.fdelay_exit()
@property
def temperature(self):
return libfdelay.fdelay_read_temperature(self.tkn)
@property
def time(self):
ts = FmcFineDelayTime()
libfdelay.fdelay_get_time(self.tkn, pointer(ts))
return ts
@time.setter
def time(self, val):
libfdelay.fdelay_set_time(self.tkn, pointer(val))
@property
def whiterabbit_mode(self):
ret = libfdelay.fdelay_check_wr_mode(self.tkn)
if ret == 0:
return True
elif ret == errno.ENODEV or \
ret == errno.ENOLINK or \
ret == errno.EAGAIN:
return False
else:
raise OSError(get_errno(),
libfdelay.fdelay_strerror(get_errno()),
"")
@whiterabbit_mode.setter
def whiterabbit_mode(self, val):
ret = libfdelay.fdelay_wr_mode(self.tkn, int(val))
if ret == errno.ENOTSUP:
raise OSError(ret,
libfdelay.fdelay_strerror(get_errno()),
"")
end = time.time() + 30
timeout = True
while time.time() < end:
time.sleep(0.1)
ret = libfdelay.fdelay_check_wr_mode(self.tkn)
if val and ret == 0:
timeout = False
break
if not val and ret == errno.ENODEV:
timeout = False
break
if timeout:
raise OSError(get_errno(), libfdelay.fdelay_strerror(get_errno()),
"")
"""
@package docstring
@author: Federico Vaga <federico.vaga@cern.ch>
SPDX-License-Identifier: LGPL-3.0-or-later
SPDX-FileCopyrightText: 2020 CERN (home.cern)
"""
from .PyFmcFineDelay import FmcFineDelay, FmcFineDelayTime, FmcFineDelayPulse, FmcFineDelayPulsePs
__all__ = (
"FmcFineDelay",
"FmcFineDelayTime",
"FmcFineDelayPulse",
"FmcFineDelayPulsePs",
)
#!/usr/bin/env python
"""
SPDX-License-Identifier: CC0-1.0
SPDX-FileCopyrightText: 2020 CERN
"""
from distutils.core import setup
setup(name='PyFmcTdc',
version='3.0',
description='Python Module to handle FMC Fine Delay devices',
author='Federico Vaga',
author_email='federico.vaga@cern.ch',
maintainer="Federico Vaga",
maintainer_email="federico.vaga@cern.ch",
url='https://www.ohwr.org/project/fmc-delay-1ns-8cha',
packages=['PyFmcFineDelay'],
license='LGPL-3.0-or-later',
)
/*
* Initializing and cleaning up the fdelay library
*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2 as published by the Free Software Foundation or, at your
* option, any later version.
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <glob.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <linux/zio.h>
#include <linux/zio-user.h>
#define FDELAY_INTERNAL
#include "fdelay-lib.h"
const char * const libfdelay_version_s = "libfdelay version: " GIT_VERSION;
const char * const libfdelay_zio_version_s = "libfdelay is using zio version: " ZIO_GIT_VERSION;
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
/**
* Initialize the fdelay library. It must be called before doing
* anything else.
* @return 0 on success, otherwise -1 and errno is appropriately set
*/
int fdelay_init(void)
{
return 0;
}
/**
* Release the resources allocated by fdelay_init(). It must be called when
* you stop to use this library. Then, you cannot use functions from this
* library anymore.
*/
void fdelay_exit(void)
{
return ;
}
/**
* It opens one specific device.
* @param[in] dev_id Fine Delay device id.
* @return an instance token, otherwise NULL and errno is appripriately set.
* ENODEV if the device was not found. EINVAL there is a mismatch with
* the arguments
*/
#define __FMCTDC_OPEN_PATH_MAX 128
struct fdelay_board *fdelay_open(int dev_id)
{
struct __fdelay_board *b = NULL;
char path[__FMCTDC_OPEN_PATH_MAX];
struct stat sb;
uint32_t v;
int ret;
if (dev_id < 0) {
errno = EINVAL;
return NULL;
}
b = malloc(sizeof(*b));
if (!b)
return NULL;
memset(b, 0, sizeof(*b));
/* get sysfs */
snprintf(path, sizeof(path),
"/sys/bus/zio/devices/fd-%04x", dev_id);
ret = stat(path, &sb);
if (ret < 0)
goto err_stat_s;
if (!S_ISDIR(sb.st_mode))
goto err_stat_s;
b->sysbase = strdup(path);
/* get dev */
snprintf(path, sizeof(path),
"/dev/zio/fd-%04x-0-0-ctrl", dev_id);
ret = stat(path, &sb);
if (ret < 0)
goto err_stat_d;
if (!S_ISCHR(sb.st_mode))
goto err_stat_d;
b->devbase = strndup(path, strlen(path) - strlen("-0-0-ctrl"));
ret = fdelay_sysfs_get(b, "version", &v);
if (ret)
goto err_version;
if (v != FDELAY_VERSION_MAJ) {
errno = FDELAY_ERR_VERSION_MISMATCH;
goto err_version;
}
return (void *)b;
err_version:
free(b->devbase);
err_stat_d:
free(b->sysbase);
err_stat_s:
free(b);
return NULL;
}
/**
* It opens one specific device using the logical unit number (CERN/CO-like)
* @param[in] lun Fine Delay LUN.
* @return an instance token, otherwise NULL and errno is appripriately set.
* ENODEV if the device was not found. EINVAL there is a mismatch with
* the arguments
*
* The function uses a symbolic link in /dev, created by the local
* installation procedure.
*/
struct fdelay_board *fdelay_open_by_lun(int lun)
{
ssize_t ret;
char dev_id_str[4];
char path_pattern[] = "/dev/fine-delay.%d";
char path[sizeof(path_pattern) + 1];
uint32_t dev_id;
if (fdelay_is_verbose())
fprintf(stderr, "called: %s(lun %i);\n", __func__, lun);
ret = snprintf(path, sizeof(path), path_pattern, lun);
if (ret < 0 || ret >= sizeof(path)) {
errno = EINVAL;
return NULL;
}
ret = readlink(path, dev_id_str, sizeof(dev_id_str));
if (ret < 0) {
errno = ENODEV;
return NULL;
}
if (sscanf(dev_id_str, "%4"SCNu32, &dev_id) != 1) {
errno = ENODEV;
return NULL;
}
return fdelay_open(dev_id);
}
/**
* Close an FMC Fine Delay device opened with one of the following functions:
* fdelay_open(), fdelay_open_by_lun()
* @param[in] userb device token
* @return 0 on success, otherwise -1 and errno is appropriately set
*/
int fdelay_close(struct fdelay_board *userb)
{
struct __fdelay_board *b = (struct __fdelay_board *)userb;
int j;
for (j = 0; j < ARRAY_SIZE(b->fdc); j++) {
if (b->fdc[j] >= 0)
close(b->fdc[j]);
}
free(b->sysbase);
free(b->devbase);
free(b);
return 0;
}
/**
* Enable or disable White-Rabbit time
* @param[in] userb device token
* @param[in] on 1 to enable, 0 to disable
* @return 0 on success, otherwise an errno code.
* ENOTSUP when White-Rabbit is not supported
*/
int fdelay_wr_mode(struct fdelay_board *userb, int on)
{
__define_board(b, userb);
if (on)
return __fdelay_command(b, FD_CMD_WR_ENABLE);
else
return __fdelay_command(b, FD_CMD_WR_DISABLE);
}
/**
* Check White-Rabbit status
* @param[in] userb device token
* @return 0 if White-Rabbit is enabled, ENOTSUP when White-Rabbit is
* not supported, ENODEV if White-Rabbit is disabled, ENOLINK if the
* White-Rabbit link is down
*/
extern int fdelay_check_wr_mode(struct fdelay_board *userb)
{
__define_board(b, userb);
if (__fdelay_command(b, FD_CMD_WR_QUERY) == 0)
return 0;
return errno;
}
/**
* Read the FMC Fine Delay temperature
* @param[in] userb device token
* @return temperature
*/
float fdelay_read_temperature(struct fdelay_board *userb)
{
uint32_t t;
__define_board(b, userb);
fdelay_sysfs_get(b, "temperature", &t);
return (float)t/16.0;
}
static const char *fdelay_error_string[] = {
[FDELAY_ERR_VERSION_MISMATCH - __FDELAY_ERR_MIN] =
"Incompatible version driver-library",
};
/**
* It returns the error message associated to the given error code
* @param[in] err error code
*/
const char *fdelay_strerror(int err)
{
if (err < __FDELAY_ERR_MIN || err > __FDELAY_ERR_MAX)
return strerror(err);
return fdelay_error_string[err - __FDELAY_ERR_MIN];
}
/*
* The "official" fine-delay API
*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2 as published by the Free Software Foundation or, at your
* option, any later version.
*/
#ifndef __FDELAY_H__
#define __FDELAY_H__
/**
* Most of the client are written in C++
*/
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#include <stdint.h>
#include "fine-delay.h"
#define __FDELAY_ERR_MIN 4096
enum fmctdc_error_numbers {
FDELAY_ERR_VERSION_MISMATCH = __FDELAY_ERR_MIN,
__FDELAY_ERR_MAX,
};
/**
* Convert the internal channel number to the one showed on the front-panel
*/
#define FDELAY_OUTPUT_HW_TO_USER(out) ((out) + 1)
/**
* Convert the channel number showed on the front-panel to the
* internal enumeration
*/
#define FDELAY_OUTPUT_USER_TO_HW(out) ((out) - 1)
/**
* Opaque data type used as device token
*/
struct fdelay_board;
/**
* Time descriptor
*/
struct fdelay_time {
uint64_t utc; /**< seconds */
uint32_t coarse; /**< 8ns step (125MHz clock)*/
uint32_t frac; /**< coarse fractional part in 1.953125ps steps */
uint32_t seq_id; /**< time-stamp sequence number, used only by the TDC */
uint32_t channel; /**< channel number, used only by the TDC
as debug information */
};
/**
* The structure used for pulse generation
*/
struct fdelay_pulse {
int mode; /**< pulse mode must be one of the followings:
FD_OUT_MODE_DISABLED, FD_OUT_MODE_DELAY,
FD_OUT_MODE_PULSE */
int rep; /**< number of pulse repetitions,
maximum 65535 or 0 for infinite */
struct fdelay_time start; /**< rasising edge time */
struct fdelay_time end; /**< falling edge time */
struct fdelay_time loop; /**< period time */
};
/**
* The alternative structure used for pulse generation
* (internally converted to the previous one)
*/
struct fdelay_pulse_ps {
int mode; /**< pulse mode must be one of the followings:
FD_OUT_MODE_DISABLED, FD_OUT_MODE_DELAY,
FD_OUT_MODE_PULSE */
int rep; /**< number of pulse repetitions,
maximum 65535 or 0 for infinite */
struct fdelay_time start; /**< rasising edge time */
uint64_t length; /**< pulse width in pico-seconds */
uint64_t period; /**< pulse period in pico-seconds */
};
extern int fdelay_init(void);
extern void fdelay_exit(void);
extern const char *fdelay_strerror(int err);
extern struct fdelay_board *fdelay_open(int dev_id);
extern struct fdelay_board *fdelay_open_by_lun(int lun);
extern int fdelay_close(struct fdelay_board *);
extern int fdelay_set_time(struct fdelay_board *b, struct fdelay_time *t);
extern int fdelay_get_time(struct fdelay_board *b, struct fdelay_time *t);
extern int fdelay_set_host_time(struct fdelay_board *b);
extern int fdelay_set_config_tdc(struct fdelay_board *b, int flags);
extern int fdelay_get_config_tdc(struct fdelay_board *b);
extern int fdelay_fread(struct fdelay_board *b, struct fdelay_time *t, int n);
extern int fdelay_fileno_tdc(struct fdelay_board *b);
extern int fdelay_read(struct fdelay_board *b, struct fdelay_time *t, int n,
int flags);
extern void fdelay_pico_to_time(uint64_t *pico, struct fdelay_time *time);
extern void fdelay_time_to_pico(struct fdelay_time *time, uint64_t *pico);
extern int fdelay_config_pulse(struct fdelay_board *b,
int channel, struct fdelay_pulse *pulse);
extern int fdelay_config_pulse_ps(struct fdelay_board *b,
int channel, struct fdelay_pulse_ps *ps);
extern int fdelay_has_triggered(struct fdelay_board *b, int channel);
extern int fdelay_wr_mode(struct fdelay_board *b, int on);
extern int fdelay_check_wr_mode(struct fdelay_board *b);
extern float fdelay_read_temperature(struct fdelay_board *b);
extern int fdelay_get_config_pulse(struct fdelay_board *userb,
int channel, struct fdelay_pulse *pulse);
extern int fdelay_get_config_pulse_ps(struct fdelay_board *userb,
int channel, struct fdelay_pulse_ps *ps);
/**
* libfmctdc version string
*/
extern const char * const libfdelay_version_s;
/**
* zio version string used during compilation of libfmctdc
*/
extern const char * const libfdelay_zio_version_s;
#ifdef FDELAY_INTERNAL /* Libray users should ignore what follows */
#include <unistd.h>
#include <fcntl.h>
#include <inttypes.h>
#include <sys/types.h>
#include <sys/stat.h>
/* Internal structure */
struct __fdelay_board {
int dev_id;
char *devbase;
char *sysbase;
int fdc[5]; /* The 5 control channels */
};
static inline int fdelay_is_verbose(void)
{
return getenv("FDELAY_LIB_VERBOSE") != 0;
}
#define __define_board(b, ub) struct __fdelay_board *b = (void *)(ub)
/* These two from ../tools/fdelay-raw.h, used internally */
static inline int __fdelay_sysfs_get(char *path, uint32_t *resp)
{
FILE *f = fopen(path, "r");
if (!f)
return -1;
errno = 0;
if (fscanf(f, "%"SCNu32, resp) != 1) {
fclose(f);
if (!errno)
errno = EINVAL;
return -1;
}
fclose(f);
return 0;
}
static inline int __fdelay_sysfs_set(char *path, uint32_t *value)
{
char s[16];
int fd, ret, len;
len = sprintf(s, "%"PRIu32"\n", *value);
fd = open(path, O_WRONLY);
if (fd < 0)
return -1;
ret = write(fd, s, len);
close(fd);
if (ret < 0)
return -1;
if (ret == len)
return 0;
errno = EINVAL;
return -1;
}
/* And these two for the board structure */
static inline int fdelay_sysfs_get(struct __fdelay_board *b, char *name,
uint32_t *resp)
{
char pathname[128];
sprintf(pathname, "%s/%s", b->sysbase, name);
return __fdelay_sysfs_get(pathname, resp);
}
static inline int fdelay_sysfs_set(struct __fdelay_board *b, char *name,
uint32_t *value)
{
char pathname[128];
sprintf(pathname, "%s/%s", b->sysbase, name);
return __fdelay_sysfs_set(pathname, value);
}
static inline int __fdelay_command(struct __fdelay_board *b, uint32_t cmd)
{
return fdelay_sysfs_set(b, "command", &cmd);
}
#endif /* FDELAY_INTERNAL */
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* __FDELAY_H__ */
/*
* output-related functions
*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2 as published by the Free Software Foundation or, at your
* option, any later version.
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <sys/select.h>
#include <linux/zio.h>
#include <linux/zio-user.h>
#define FDELAY_INTERNAL
#include "fdelay-lib.h"
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
/**
* Convert pico-seconds into a `struct fdelay_time` data structure
* @param[in] pico pico-second to convert
* @param[out] time destination data structure
*/
void fdelay_pico_to_time(uint64_t *pico, struct fdelay_time *time)
{
uint64_t p = *pico;
time->utc = p / (1000ULL * 1000ULL * 1000ULL * 1000ULL);
p %= (1000ULL * 1000ULL * 1000ULL * 1000ULL);
time->coarse = p / 8000;
p %= 8000;
time->frac = p * 4096 / 8000;
}
/**
* Convert a `struct fdelay_time` data structure into pico-seconds
* @param[in] time time to convert
* @param[out] pico destination variable
*/
void fdelay_time_to_pico(struct fdelay_time *time, uint64_t *pico)
{
uint64_t p;
p = time->frac * 8000 / 4096;
p += (uint64_t) time->coarse * 8000LL;
p += time->utc * (1000ULL * 1000ULL * 1000ULL * 1000ULL);
*pico = p;
}
static int __fdelay_get_ch_fd(struct __fdelay_board *b,
int channel, int *fdc)
{
int ch14 = channel + 1;
if (channel < 0 || channel > 3) {
errno = EINVAL;
return -1;
}
if (b->fdc[ch14] <= 0) {
char fname[128];
sprintf(fname, "%s-%i-0-ctrl", b->devbase, ch14);
b->fdc[ch14] = open(fname, O_WRONLY | O_NONBLOCK);
if (b->fdc[ch14] < 0)
return -1;
}
*fdc = b->fdc[ch14];
return 0;
}
/**
* Configure an FMC Fine Delay channel to produce a pulse
* @param[in] userb device token
* @param[in] channel channel number in range [0, 3] ([1,4] on the front-panel)
* @param[in] pulse pulse descriptor
* @return 0 on success, otherwise -1 and errno is appropriately set
*/
int fdelay_config_pulse(struct fdelay_board *userb,
int channel, struct fdelay_pulse *pulse)
{
__define_board(b, userb);
struct zio_control ctrl = {0,};
uint32_t *a;
int fdc;
if (__fdelay_get_ch_fd(b, channel, &fdc) < 0)
return -1; /* errno already set */
a = ctrl.attr_channel.ext_val;
a[FD_ATTR_OUT_MODE] = pulse->mode & 0x7f;
a[FD_ATTR_OUT_REP] = pulse->rep;
a[FD_ATTR_OUT_START_H] = pulse->start.utc >> 32;
a[FD_ATTR_OUT_START_L] = pulse->start.utc;
a[FD_ATTR_OUT_START_COARSE] = pulse->start.coarse;
a[FD_ATTR_OUT_START_FINE] = pulse->start.frac;
a[FD_ATTR_OUT_END_H] = pulse->end.utc >> 32;
a[FD_ATTR_OUT_END_L] = pulse->end.utc;
a[FD_ATTR_OUT_END_COARSE] = pulse->end.coarse;
a[FD_ATTR_OUT_END_FINE] = pulse->end.frac;
a[FD_ATTR_OUT_DELTA_L] = pulse->loop.utc; /* only 0..f */
a[FD_ATTR_OUT_DELTA_COARSE] = pulse->loop.coarse; /* only 0..f */
a[FD_ATTR_OUT_DELTA_FINE] = pulse->loop.frac; /* only 0..f */
int mode = pulse->mode & 0x7f;
/* hotfix: the ZIO has a bug blocking the output when the output raw_io function returns an error.
therefore we temporarily have to check the output programming correctness in the user library. */
if (mode == FD_OUT_MODE_DELAY || mode == FD_OUT_MODE_DISABLED)
{
if(pulse->rep < 0 || pulse->rep > 16) /* delay mode allows trains of 1 to 16 pulses. */
return -EINVAL;
if(a[FD_ATTR_OUT_START_L] == 0 && a[FD_ATTR_OUT_START_COARSE] < (600 / 8)) // 600 ns min delay
return -EINVAL;
}
/* we need to fill the nsample field of the control */
ctrl.attr_trigger.std_val[1] = 1;
ctrl.nsamples = 1;
ctrl.ssize = 4;
ctrl.nbits = 32;
write(fdc, &ctrl, sizeof(ctrl));
return 0;
}
static void fdelay_add_ps(struct fdelay_time *p, uint64_t ps)
{
uint32_t coarse, frac;
/* FIXME: this silently fails with ps > 10^12 = 1s */
coarse = ps / 8000;
frac = ((ps % 8000) << 12) / 8000;
p->frac += frac;
if (p->frac >= 4096) {
p->frac -= 4096;
coarse++;
}
p->coarse += coarse;
if (p->coarse >= 125*1000*1000) {
p->coarse -= 125*1000*1000;
p->utc++;
}
}
static void fdelay_sub_ps(struct fdelay_time *p, uint64_t ps)
{
uint32_t coarse_neg, frac_neg;
/* FIXME: this silently fails with ps > 10^12 = 1s */
coarse_neg = ps / 8000;
frac_neg = ((ps % 8000) << 12) / 8000;
if (p->frac < frac_neg) {
p->frac += 4096;
coarse_neg++;
}
p->frac -= frac_neg;
if (p->coarse < coarse_neg) {
p->coarse += 125*1000*1000;
p->utc--;
}
p->coarse -= coarse_neg;
}
static void fdelay_add_signed_ps(struct fdelay_time *p, signed ps)
{
if (ps > 0)
fdelay_add_ps(p, ps);
else
fdelay_sub_ps(p, -ps);
}
/**
* Configure an FMC Fine Delay channel to produce a pulse
* @param[in] userb device token
* @param[in] channel channel number in range [0, 3] ([1,4] on the front-panel)
* @param[in] ps pulse descriptor
* @return 0 on success, otherwise -1 and errno is appropriately set
*
* This is a variant of fdelay_config_pulse() using a different pulse
* descriptor where pulse width and period are expressed in pico-seconds
*/
int fdelay_config_pulse_ps(struct fdelay_board *userb,
int channel, struct fdelay_pulse_ps *ps)
{
struct fdelay_pulse p;
p.mode = ps->mode;
p.rep = ps->rep;
p.start = ps->start;
p.end = ps->start;
fdelay_add_ps(&p.end, ps->length);
fdelay_pico_to_time(&ps->period, &p.loop);
return fdelay_config_pulse(userb, channel, &p);
}
/**
* Retrieve the current FMC Fine-Delay channel configuration
* @param[in] userb device token
* @param[in] channel channel number in range [0, 3] ([1,4] on the front-panel)
* @param[out] pulse pulse descriptor
* @return 0 on success, otherwise -1 and errno is appropriately set
*/
int fdelay_get_config_pulse(struct fdelay_board *userb,
int channel, struct fdelay_pulse *pulse)
{
__define_board(b, userb);
char s[32];
uint32_t utc_h, utc_l, tmp;
uint32_t input_offset, output_offset, output_user_offset;
memset(pulse, 0, sizeof(struct fdelay_pulse));
sprintf(s,"fd-ch%i/%s", channel + 1, "mode");
if (fdelay_sysfs_get(b, s, &tmp) < 0)
return -1; /* errno already set */
pulse->mode = tmp;
sprintf(s,"fd-ch%i/%s", channel + 1, "rep");
if (fdelay_sysfs_get(b, s, &tmp) < 0)
return -1;
pulse->rep = tmp;
sprintf(s,"fd-ch%i/%s", channel + 1, "start-h");
if (fdelay_sysfs_get(b, s, &utc_h) < 0)
return -1;
sprintf(s,"fd-ch%i/%s", channel + 1, "start-l");
if (fdelay_sysfs_get(b, s, &utc_l) < 0)
return -1;
pulse->start.utc = (((uint64_t)utc_h) << 32) | utc_l;
sprintf(s,"fd-ch%i/%s", channel + 1, "start-coarse");
if (fdelay_sysfs_get(b, s, &pulse->start.coarse) < 0)
return -1;
sprintf(s,"fd-ch%i/%s", channel + 1, "start-fine");
if (fdelay_sysfs_get(b, s, &pulse->start.frac) < 0)
return -1;
sprintf(s,"fd-ch%i/%s", channel + 1, "end-h");
if (fdelay_sysfs_get(b, s, &utc_h) < 0)
return -1;
sprintf(s,"fd-ch%i/%s", channel + 1, "end-l");
if (fdelay_sysfs_get(b, s, &utc_l) < 0)
return -1;
pulse->end.utc = (((uint64_t)utc_h) << 32) | utc_l;
sprintf(s,"fd-ch%i/%s", channel + 1, "end-coarse");
if (fdelay_sysfs_get(b, s, &pulse->end.coarse) < 0)
return -1;
sprintf(s,"fd-ch%i/%s", channel + 1, "end-fine");
if (fdelay_sysfs_get(b, s, &pulse->end.frac) < 0)
return -1;
sprintf(s,"fd-ch%i/%s", channel + 1, "delta-l");
if (fdelay_sysfs_get(b, s, &utc_l) < 0)
return -1;
pulse->loop.utc = utc_l;
sprintf(s,"fd-ch%i/%s", channel + 1, "delta-coarse");
if (fdelay_sysfs_get(b, s, &pulse->loop.coarse) < 0)
return -1;
sprintf(s,"fd-ch%i/%s", channel + 1, "delta-fine");
if (fdelay_sysfs_get(b, s, &pulse->loop.frac) < 0)
return -1;
/*
* Now, to return consistent values to the user, we must
* un-apply all offsets that the driver added
*/
sprintf(s,"fd-ch%i/%s", channel + 1, "delay-offset");
if (fdelay_sysfs_get(b, s, &output_offset) < 0)
return -1;
sprintf(s,"fd-ch%i/%s", channel + 1, "user-offset");
if (fdelay_sysfs_get(b, s, &output_user_offset) < 0)
return -1;
sprintf(s,"fd-input/%s", "offset");
if (fdelay_sysfs_get(b, s, &input_offset) < 0)
return -1;
int m = pulse->mode & 0x7f;
switch(m)
{
case FD_OUT_MODE_DISABLED:
/* hack for Steen/COHAL: if channel is disabled, apply delay-mode offsets */
case FD_OUT_MODE_DELAY:
fdelay_add_signed_ps(&pulse->start, -(signed)output_offset);
fdelay_add_signed_ps(&pulse->end, -(signed)output_offset);
fdelay_add_signed_ps(&pulse->start, -(signed)output_user_offset);
fdelay_add_signed_ps(&pulse->end, -(signed)output_user_offset);
fdelay_add_signed_ps(&pulse->start, -(signed)input_offset);
fdelay_add_signed_ps(&pulse->end, -(signed)input_offset);
break;
case FD_OUT_MODE_PULSE:
fdelay_add_signed_ps(&pulse->start, -(signed)output_offset);
fdelay_add_signed_ps(&pulse->end, -(signed)output_offset);
fdelay_add_signed_ps(&pulse->start, -(signed)output_user_offset);
fdelay_add_signed_ps(&pulse->end, -(signed)output_user_offset);
break;
}
return 0;
}
static void fdelay_subtract_ps(struct fdelay_time *t2,
struct fdelay_time *t1, int64_t *pico)
{
uint64_t pico1, pico2;
fdelay_time_to_pico(t2, &pico2);
fdelay_time_to_pico(t1, &pico1);
*pico = (int64_t)pico2 - pico1;
}
/**
* Retrieve the current FMC Fine-Delay channel configuration
* @param[in] userb device token
* @param[in] channel channel number in range [0, 3] ([1,4] on the front-panel)
* @param[out] ps pulse descriptor
* @return 0 on success, otherwise -1 and errno is appropriately set
*
* This is a variant of fdelay_get_config_pulse() using a different pulse
* descriptor where pulse width and period are expressed in pico-seconds
*/
int fdelay_get_config_pulse_ps(struct fdelay_board *userb,
int channel, struct fdelay_pulse_ps *ps)
{
struct fdelay_pulse pulse;
if (fdelay_get_config_pulse(userb, channel, &pulse) < 0)
return -1;
memset(ps, 0, sizeof(struct fdelay_pulse_ps));
ps->mode = pulse.mode;
ps->rep = pulse.rep;
ps->start = pulse.start;
/* FIXME: subtraction can be < 0 */
fdelay_subtract_ps(&pulse.end, &pulse.start, (int64_t *)&ps->length);
fdelay_time_to_pico(&pulse.loop, &ps->period);
return 0;
}
/**
* Retrieve the current FMC Fine-Delay channel configuration
* @param[in] userb device token
* @param[in] channel channel number in range [0, 3] ([1,4] on the front-panel)
* @return 1 if trigger did happen, 0 if trigget did not happen,
* otherwise -1 and errno is appropriately set
*/
int fdelay_has_triggered(struct fdelay_board *userb, int channel)
{
__define_board(b, userb);
char s[32];
uint32_t mode;
sprintf(s,"fd-ch%i/mode", channel + 1);
if (fdelay_sysfs_get(b, s, &mode) < 0)
return -1; /* errno already set */
return (mode & 0x80) != 0;
}
/*
* TDC-related functions
*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2 as published by the Free Software Foundation or, at your
* option, any later version.
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/select.h>
#include <linux/zio.h>
#include <linux/zio-user.h>
#define FDELAY_INTERNAL
#include "fdelay-lib.h"
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
static int config_mask =
FD_TDCF_DISABLE_INPUT |
FD_TDCF_DISABLE_TSTAMP |
FD_TDCF_TERM_50;
/**
* Configure TDC options
* @param[in] userb device token
* @param[in] flags is a bit-mask of FD_TDCF_* flags
* @return 0 on success, otherwise -1 and errno is appropriately set.
*/
int fdelay_set_config_tdc(struct fdelay_board *userb, int flags)
{
__define_board(b, userb);
uint32_t val;
if (flags & ~config_mask) {
errno = EINVAL;
return -1;
}
val = flags;
return fdelay_sysfs_set(b, "fd-input/flags", &val);
}
/**
* Configure TDC options
* @param[in] userb device token
* @return on success, a bit-mask of FD_TDCF_* flags; otherwise -1 and errno
* is appropriately set.
*/
int fdelay_get_config_tdc(struct fdelay_board *userb)
{
__define_board(b, userb);
uint32_t val;
int ret;
ret = fdelay_sysfs_get(b, "fd-input/flags", &val);
if (ret) return ret;
return val;
}
static int __fdelay_open_tdc(struct __fdelay_board *b)
{
if (b->fdc[0] <= 0) {
char fname[128];
sprintf(fname, "%s-0-0-ctrl", b->devbase);
b->fdc[0] = open(fname, O_RDONLY | O_NONBLOCK);
}
return b->fdc[0];
}
/**
* Get TDC file descriptor
* @param[in] userb device token
* @return on success, a valid file descriptor; otherwise -1 and errno
* is appropriately set.
*
* This returns the file descriptor associated to the TDC device,
* so you can *select* or *poll* before calling *fdelay_read*.
*/
int fdelay_fileno_tdc(struct fdelay_board *userb)
{
__define_board(b, userb);
return __fdelay_open_tdc(b);
}
/**
* Read TDC timestamps
* @param[in] userb device token
* @param[out] t buffer for timestamps
* @param[in] n maximum number that t can store
* @param[in] flags for options: O_NONBLOCK for non blocking read
* @return the number of valid timestamps in the buffer, otherwise -1
* and errno is appropriately set. EAGAIN if the driver buffer is
* empty
*/
int fdelay_read(struct fdelay_board *userb, struct fdelay_time *t, int n,
int flags)
{
__define_board(b, userb);
struct zio_control ctrl;
uint32_t *attrs;
int i, fd;
fd_set set;
fd = __fdelay_open_tdc(b);
if (fd < 0)
return fd; /* errno already set */
for (i = 0; i < n;) {
int j;
j = read(fd, &ctrl, sizeof(ctrl));
if (j < 0 && errno != EAGAIN)
return -1;
if (j == sizeof(ctrl)) {
/* one sample: pick it */
attrs = ctrl.attr_channel.ext_val;
t->utc = (uint64_t)attrs[FD_ATTR_TDC_UTC_H] << 32
| attrs[FD_ATTR_TDC_UTC_L];
t->coarse = attrs[FD_ATTR_TDC_COARSE];
t->frac = attrs[FD_ATTR_TDC_FRAC];
t->seq_id = attrs[FD_ATTR_TDC_SEQ];
t->channel = attrs[FD_ATTR_TDC_CHAN];
t++;
i++;
continue;
}
if (j > 0) {
errno = EIO;
return -1;
}
/* so, it's EAGAIN: if we already got something, we are done */
if (i)
return i;
/* EAGAIN at first sample */
if (j < 0 && flags == O_NONBLOCK)
return -1;
/* So, first sample and blocking read. Wait.. */
FD_ZERO(&set);
FD_SET(fd, &set);
if (select(fd+1, &set, NULL, NULL, NULL) < 0)
return -1;
continue;
}
return i;
}
/**
* Read TDC timestamps
* @param[in] userb device token
* @param[out] t buffer for timestamps
* @param[in] n maximum number that t can store
* @return the number of valid timestamps in the buffer, otherwise -1
* and errno is appropriately set.
*
* The function behaves like *fread*: it tries to read all samples,
* even if it implies sleeping several times. Use it only if you are
* aware that all the expected pulses will reach you.
*/
int fdelay_fread(struct fdelay_board *userb, struct fdelay_time *t, int n)
{
int i;
for (i = 0; i < n; ) {
int loop;
loop = fdelay_read(userb, t + i, n - i, 0);
if (loop < 0)
return -1;
i += loop;
}
return i;
}
/*
* Time-related functions
*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2 as published by the Free Software Foundation or, at your
* option, any later version.
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <linux/zio.h>
#include <linux/zio-user.h>
#define FDELAY_INTERNAL
#include "fdelay-lib.h"
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
static char *names[] = {
"utc-h",
"utc-l",
"coarse"
};
/**
* Set board time
* @param[in] userb device token
* @param[in] t user time
* @return 0 on success, otherwise -1 and errno is appropriately set.
*
* It only uses the fields *utc* and *coarse*.
*/
int fdelay_set_time(struct fdelay_board *userb, struct fdelay_time *t)
{
__define_board(b, userb);
uint32_t attrs[ARRAY_SIZE(names)];
int i;
attrs[0] = t->utc >> 32;
attrs[1] = t->utc;
attrs[2] = t->coarse;
for (i = ARRAY_SIZE(names) - 1; i >= 0; i--)
if (fdelay_sysfs_set(b, names[i], attrs + i) < 0)
return -1;
return 0;
}
/**
* Get board time
* @param[in] userb device token
* @param[out] t board time
* @return 0 on success, otherwise -1 and errno is appropriately set.
*
* It only uses the fields *utc* and *coarse*.
*/
int fdelay_get_time(struct fdelay_board *userb, struct fdelay_time *t)
{
__define_board(b, userb);
uint32_t attrs[ARRAY_SIZE(names)];
int i;
for (i = 0; i < ARRAY_SIZE(names); i++)
if (fdelay_sysfs_get(b, names[i], attrs + i) < 0)
return -1;
t->utc = (long long)attrs[0] << 32;
t->utc += attrs[1];
t->coarse = attrs[2];
return 0;
}
/**
* Set board time to host time
* @param[in] userb device token
* @return 0 on success, otherwise -1 and errno is appropriately set.
*
* The precision should be in the order of 1 microsecond, but will drift over
* time. This function is only provided to coarsely correlate the board time
* with the system time. Relying on system time for synchronizing multiple
* *fine-delays* is strongly discouraged.
*/
int fdelay_set_host_time(struct fdelay_board *userb)
{
__define_board(b, userb);
return __fdelay_command(b, FD_CMD_HOST_TIME);
}
fd-raw-input
parport-burst
fd-raw-gettime
fd-raw-settime
fd-raw-output
fd-raw-perf
fdelay-list
fdelay-term
fdelay-board-time
fdelay-open-by-lun
fdelay-read
fdelay-fread
fdelay-pulse
fdelay-pulse-tom
# older user-space tools for spec-fine-delay
# If it exists includes Makefile.specific. In this Makefile, you should put
# specific Makefile code that you want to run before this. For example,
# build a particular environment.
-include Makefile.specific
M = $(shell /bin/pwd)/../kernel
HOST_EXTRACFLAGS += -I$(M) -I../lib -I../zio/include -Wno-trigraphs -Wall -ggdb
HOSTCC ?= gcc
hostprogs-y := fd-raw-input
hostprogs-y += fd-raw-gettime
hostprogs-y += fd-raw-settime
hostprogs-y += parport-burst
hostprogs-y += fd-raw-output
hostprogs-y += fd-raw-perf
# we are not in the kernel, so we need to piggy-back on "make modules"
all modules: $(hostprogs-y)
clean:
rm -f $(hostprogs-y) *.o *~
# make nothing for modules_install, but avoid errors
modules_install install:
# we need this as we are out of the kernel
%: %.c
$(HOSTCC) $(HOST_EXTRACFLAGS) -O2 -Wall $^ -L../lib -lfdelay -o $@
/*
* a tool to get the fdelay time from sysfs
*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2 as published by the Free Software Foundation or, at your
* option, any later version.
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <linux/zio.h>
#include <linux/zio-user.h>
#include <fine-delay.h>
#include "fdelay-raw.h"
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
struct time_word {
uint32_t val;
char *name;
};
struct time_word words[] = {
{0, "utc-h"}, /* This must be first, as this forces hardware access */
{0, "utc-l"},
{0, "coarse"},
};
int main(int argc, char **argv)
{
char *sysnames[10];
char path[80];
int i, err;
i = fdelay_get_sysnames(sysnames);
if (!i) {
fprintf(stderr, "%s: no fine-delay devices\n", argv[0]);
exit(1);
}
if (i > 1) {
fprintf(stderr, "%s: several fine-delay devices, using %s\n",
argv[0], sysnames[0]);
}
for (i = 0, err = 0; i < ARRAY_SIZE(words); i++) {
sprintf(path, "%s/%s", sysnames[0], words[i].name);
if (fdelay_sysfs_get(path, &words[i].val) != 0)
err++;
}
if (err) {
fprintf(stderr, "%s: got %i errors reading %zu attributes\n",
argv[0], err, ARRAY_SIZE(words));
}
printf("%i.%09li\n", words[1].val, (long)words[2].val * 8);
return 0;
}
/*
* an input tools that accesses the ZIO device
*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2 as published by the Free Software Foundation or, at your
* option, any later version.
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <glob.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <time.h>
#include <inttypes.h>
#include <sys/time.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <linux/zio.h>
#include <linux/zio-user.h>
#include <fine-delay.h>
enum {
MODE_HEX = 1,
MODE_FLOAT = 2,
MODE_PICO = 4
};
int expect;
int show_time;
void event(uint32_t *a, char *name, int *seq, int modemask, long double *t1,
uint64_t *p1)
{
int sequence = a[FD_ATTR_TDC_SEQ];
long double t2;
int64_t p2, delta;
static int64_t guess;
if (*seq != -1) {
if (sequence != ((*seq + 1) & 0xffff)) {
printf("%s: LOST %i events\n", name,
(sequence - (*seq + 1)) & 0xffff);
*t1 = 0.0;
*p1 = 0LL;
}
}
*seq = sequence;
if (show_time) {
static struct timeval tv, otv;
int deltamicro;
gettimeofday(&tv, NULL);
if (otv.tv_sec) {
deltamicro = (tv.tv_sec - otv.tv_sec) * 1000 * 1000
+ tv.tv_usec - otv.tv_usec;
printf("+ %i.%06i:", deltamicro / 1000 / 1000,
deltamicro % (1000*1000));
} else {
printf("%03li.%06li:", tv.tv_sec % 1000, tv.tv_usec);
}
otv = tv;
} else {
/* time works with one file only, avoid the fname */
printf("%s:", name);
}
if (modemask & MODE_HEX) {
printf(" %08x %08x %08x %08x %08x\n",
a[FD_ATTR_TDC_UTC_H],
a[FD_ATTR_TDC_UTC_L],
a[FD_ATTR_TDC_COARSE],
a[FD_ATTR_TDC_FRAC],
a[FD_ATTR_TDC_SEQ]);
}
if (modemask & MODE_FLOAT) {
t2 = a[FD_ATTR_TDC_UTC_L] + a[FD_ATTR_TDC_COARSE]
* .000000008; /* 8ns */
if (*t1) {
printf(" %17.9Lf (delta %13.9Lf)\n",
t2, t2 - *t1);
} else {
printf(" %17.9Lf\n", t2);
}
*t1 = t2;
}
if (modemask & MODE_PICO) {
p2 = a[FD_ATTR_TDC_COARSE] * 8000LL
+ a[FD_ATTR_TDC_FRAC] * 8000LL / 4096;
delta = p2 - *p1;
if (delta < 0)
delta += 1000LL * 1000 * 1000 * 1000;
if (*p1) {
printf(" %012"PRIu64" - delta %012"PRIu64"", p2, delta);
if (expect) {
guess += expect;
if (guess > 1000LL * 1000 * 1000 * 1000)
guess -= 1000LL * 1000 * 1000 * 1000;
printf(" - error %6i", (int)(p2 - guess));
}
putchar('\n');
}
else {
printf(" %012"PRIu64"\n", p2);
if (expect)
guess = p2;
}
*p1 = p2;
}
}
#define MAXFD 16
struct zio_control ctrl;
int main(int argc, char **argv)
{
glob_t glob_buf;
int i, j, tout = 0, maxfd = 0;
int fd[MAXFD], seq[MAXFD];
struct timeval tv, *tvp = NULL;
fd_set allset, curset;
int modemask = MODE_HEX;
long double t1[MAXFD] = {0.0,};
uint64_t p1[MAXFD] = {0LL,};
uint32_t *attrs;
if (getenv("FD_EXPECTED_RATE"))
expect = atoi(getenv("FD_EXPECTED_RATE"));
if (getenv("FD_SHOW_TIME"))
show_time = 1;
while ((i = getopt(argc, argv, "fprht:")) != -1) {
switch(i) {
case 'f':
modemask &= ~MODE_HEX;
modemask |= MODE_FLOAT;
break;
case 'p':
modemask &= ~MODE_HEX;
modemask |= MODE_PICO;
break;
case 'r':
case 'h':
modemask |= MODE_HEX;
break;
case 't':
tout = atoi(optarg);
break;
}
}
/* adjust for consumed arguments */
argv[optind - 1] = argv[0];
argc -= (optind - 1);
argv += (optind - 1);
if (argc < 2) {
/* rebuild argv using globbing */
glob_buf.gl_offs = 1;
/* "????" spits a trigraph warning: use "*" instead (bah!) */
glob("/dev/fd-*-0-0-ctrl", GLOB_DOOFFS, NULL, &glob_buf);
glob("/dev/zio/fd-*-0-0-ctrl",
GLOB_DOOFFS | GLOB_APPEND, NULL, &glob_buf);
glob("/dev/zio/zio-fd-*-0-0-ctrl",
GLOB_DOOFFS | GLOB_APPEND, NULL, &glob_buf);
glob_buf.gl_pathv[0] = argv[0];
argv = glob_buf.gl_pathv;
argc = glob_buf.gl_pathc + glob_buf.gl_offs;
}
if (argc == 1) {
fprintf(stderr, "%s: no arguments and no glob results\n",
argv[0]);
exit(1);
};
if (argc >= MAXFD) {
fprintf(stderr, "%s: too many file names\n", argv[0]);
exit(1);
};
FD_ZERO(&allset);
for (i = 1; i < argc; i++) {
fd[i] = open(argv[i], O_RDONLY);
if (fd[i] < 0) {
fprintf(stderr, "%s: %s: %s\n", argv[0], argv[1],
strerror(errno));
exit(1);
}
if (fd[i] > maxfd)
maxfd = fd[i];
FD_SET(fd[i], &allset);
seq[i] = -1;
}
if (tout == 0)
setlinebuf(stdout);
tvp = NULL;
/* Ok, now wait for each of them to spit a timestamp */
while (1) {
curset = allset;
switch(select(maxfd + 1, &curset, NULL, NULL, tvp)) {
case -1:
if (errno == EINTR)
continue;
fprintf(stderr, "%s: select: %s\n", argv[0],
strerror(errno));
exit(1);
case 0:
exit(0);
}
if (tout) {
/* prepare timeout for next time */
tv.tv_sec = tout / (1000 * 1000);
tv.tv_usec = tout % (1000 * 1000);
tvp = &tv;
}
for (i = 1; i < argc; i++) {
if (!FD_ISSET(fd[i], &curset))
continue;
/* Ok, it's there: read and decode */
j = read(fd[i], &ctrl, sizeof(ctrl));
if (j != sizeof(ctrl)) {
fprintf(stderr, "%s: read(): got %i not %zu\n",
argv[0], j, sizeof(ctrl));
exit(1);
}
attrs = ctrl.attr_channel.ext_val;
event(attrs, argv[i], seq + i, modemask,
t1 + i, p1 + i);
}
}
return 0;
}
/*
* an output tools that accesses the ZIO device
*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2 as published by the Free Software Foundation or, at your
* option, any later version.
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <glob.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <time.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <linux/zio.h>
#include <linux/zio-user.h>
#include <fine-delay.h>
int main(int argc, char **argv)
{
glob_t glob_buf;
struct zio_control ctrl;
int fdc;
char *s;
int i, j, val, ch;
uint32_t *attrs;
/* glob to find the device; use the first */
glob("/dev/fd-*-1-0-ctrl", 0, NULL, &glob_buf);
glob("/dev/zio/fd-*-1-0-ctrl", GLOB_APPEND, NULL, &glob_buf);
glob("/dev/zio/zio-fd-*-1-0-ctrl", GLOB_APPEND, NULL, &glob_buf);
if (glob_buf.gl_pathc != 1) {
fprintf(stderr, "%s: found %zu devices, need 1 only\n",
argv[0], glob_buf.gl_pathc);
exit(1);
}
s = glob_buf.gl_pathv[0];
if (getenv("CHAN")) {
/* Hack: change the channel */
ch = atoi(getenv("CHAN"));
if (ch)
s[strlen(s)-strlen("1-0-ctrl")] = '0' + ch;
}
fdc = open(s, O_WRONLY);
if (fdc < 0) {
fprintf(stderr, "%s: %s: %s\n", argv[0], s, strerror(errno));
exit(1);
}
memset(&ctrl, 0, sizeof(ctrl));
attrs = ctrl.attr_channel.ext_val;
for (i = 1; i < argc; i++) {
j = i - 1 + FD_ATTR_DEV__LAST;
if (sscanf(argv[i], "+%i", &val) == 1) {
val += time(NULL);
} else if (sscanf(argv[i], "%i", &val) != 1) {
fprintf(stderr, "%s: not a number \"%s\"\n", argv[0],
argv[i]);
exit(1);
}
attrs[j] = val;
}
/* we need to fill the nsample field of the control */
ctrl.attr_trigger.std_val[1] = 1;
ctrl.nsamples = 1;
ctrl.ssize = 4;
ctrl.nbits = 32;
write(fdc, &ctrl, sizeof(ctrl));
exit(0);
}
/*
* an input tool that measures performance
* (almost a copy of fd-raw-input, even if code repetition is BAD)
*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2 as published by the Free Software Foundation or, at your
* option, any later version.
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <glob.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <sys/time.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <linux/zio.h>
#include <linux/zio-user.h>
#include <fine-delay.h>
/*
* Lazily, we ignore the "seconds" part, and assume we run at 5Hz minimum
* so each picosecond count fits in 32 bits (0.4s)
*/
struct fd_perf {
/* sequence is 16-bits, do the same here */
uint16_t first, prev; /* sequence numbers: -1 == "not in a burst" */
struct timeval tv;
uint nev;
uint64_t pico_tot, micro_tot;
int64_t pico_prev, pico_min, pico_max, pico_avg;
uint32_t micro_prev, micro_min, micro_max, micro_avg;
int lost;
};
static void perf_clean(struct fd_perf *p)
{
p->pico_tot = p->micro_tot = 0;
p->pico_min = p->micro_min = ~0;
p->pico_max = p->micro_max = 0;
p->nev = p->lost = 0;
}
static void perf_one(struct fd_perf *p, uint32_t *a /* attrs */)
{
int64_t pico, micro, diff;
gettimeofday(&p->tv, NULL);
pico = a[FD_ATTR_TDC_COARSE] * 8000LL
+ (a[FD_ATTR_TDC_FRAC] << 12) / 8000;
micro = p->tv.tv_usec;
if (!p->nev) {
p->first = a[FD_ATTR_TDC_SEQ];
goto set_prev;
}
p->lost += (int16_t)(a[FD_ATTR_TDC_SEQ] - p->prev - 1);
/* count hardware-reported pico */
diff = pico - p->pico_prev;
if (0) {
printf("%"PRIi64" = %f - %f\n", diff,
pico/1000000.0, p->pico_prev/1000000.0);
}
if (diff < 0)
diff += 1000LL * 1000 * 1000 * 1000;
if (diff < p->pico_min)
p->pico_min = diff;
if (diff > p->pico_max)
p->pico_max = diff;
p->pico_tot += diff;
/* count software-reported micro */
diff = micro - p->micro_prev;
if (diff < 0)
diff += 1000LL * 1000;
if (diff < p->micro_min)
p->micro_min = diff;
if (diff > p->micro_max)
p->micro_max = diff;
p->micro_tot += diff;
set_prev:
p->nev++;
p->prev = a[FD_ATTR_TDC_SEQ];
p->micro_prev = micro;
p->pico_prev = pico;
}
static void perf_report_clean(struct fd_perf *p)
{
if (p->prev < 0 || !p->nev)
return;
p->pico_avg = p->pico_tot / (p->nev - 1);
p->micro_avg = p->micro_tot / (p->nev - 1);
printf("%i pulses (%i lost)\n", p->nev, p->lost);
printf(" hw: %"PRIi64"ps (%fkHz) -- min %"PRIi64" max %"PRIi64" delta %"PRIi64"\n",
p->pico_avg, 1000.0 * 1000 * 1000 / p->pico_avg,
p->pico_min, p->pico_max, p->pico_max - p->pico_min);
printf(" sw: %ius (%fkHz) -- min %i max %i delta %i\n",
p->micro_avg, 1000.0 / p->micro_avg,
p->micro_min, p->micro_max, p->micro_max - p->micro_min);
printf("\n");
perf_clean(p);
}
#define MAXFD 16
struct zio_control ctrl;
int main(int argc, char **argv)
{
glob_t glob_buf;
int fd[MAXFD], seq[MAXFD];
int i, j, maxfd = 0;
fd_set allset, curset;
struct timeval tout;
struct fd_perf perf = {0,};
int floatmode = 0;
uint32_t *attrs;
int step = 0;
if (getenv("PERF_STEP"))
step = atoi(getenv("PERF_STEP"));
if (argc > 1 && !strcmp(argv[1], "-f")) {
floatmode = 1;
argv[1] = argv[0];
argv++, argc--;
}
if (argc < 2) {
/* rebuild argv using globbing */
glob_buf.gl_offs = 1;
/* "????" spits a trigraph warning: use "*" instead (bah!) */
glob("/dev/fd-*-0-0-ctrl", GLOB_DOOFFS, NULL, &glob_buf);
glob("/dev/zio/fd-*-0-0-ctrl",
GLOB_DOOFFS | GLOB_APPEND, NULL, &glob_buf);
glob("/dev/zio/zio-fd-*-0-0-ctrl",
GLOB_DOOFFS | GLOB_APPEND, NULL, &glob_buf);
glob_buf.gl_pathv[0] = argv[0];
argv = glob_buf.gl_pathv;
argc = glob_buf.gl_pathc + glob_buf.gl_offs;
}
if (argc == 1) {
fprintf(stderr, "%s: no arguments and no glob results\n",
argv[0]);
exit(1);
};
if (argc > 2) {
fprintf(stderr, "%s: too many devices, using only \"%s\"\n",
argv[0], argv[1]);
argc = 2;
/* So the loops below are unchanged from fd-raw-input */
};
FD_ZERO(&allset);
for (i = 1; i < 2; i++) {
fd[i] = open(argv[i], O_RDONLY);
if (fd[i] < 0) {
fprintf(stderr, "%s: %s: %s\n", argv[0], argv[1],
strerror(errno));
exit(1);
}
if (fd[i] > maxfd)
maxfd = fd[i];
FD_SET(fd[i], &allset);
seq[i] = -1;
}
/* Ok, now wait for each of them to spit a timestamp */
setlinebuf(stdout);
perf_clean(&perf);
while (1) {
curset = allset;
tout.tv_sec = 0;
tout.tv_usec = 300*1000; /* After 300ms the burst is over */
switch(select(maxfd + 1, &curset, NULL, NULL, &tout)) {
case -1:
if (errno == EINTR)
continue;
fprintf(stderr, "%s: select: %s\n", argv[0],
strerror(errno));
exit(1);
case 0:
perf_report_clean(&perf);
continue;
}
for (i = 1; i < argc; i++) {
if (!FD_ISSET(fd[i], &curset))
continue;
/* Ok, it's there: read and decode */
j = read(fd[i], &ctrl, sizeof(ctrl));
if (j != sizeof(ctrl)) {
fprintf(stderr, "%s: read(): got %i not %zu\n",
argv[0], j, sizeof(ctrl));
exit(1);
}
attrs = ctrl.attr_channel.ext_val;
perf_one(&perf, attrs);
}
if (step && perf.nev) {
static time_t t;
/* don't make an extra system call, use perf.tv */
if (perf.nev == 1)
t = perf.tv.tv_sec;
else
if (perf.tv.tv_sec - t >= step)
perf_report_clean(&perf);
}
}
return 0;
}
/*
* a tool to set the fdelay time through sysfs
*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2 as published by the Free Software Foundation or, at your
* option, any later version.
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <linux/zio.h>
#include <linux/zio-user.h>
#include <fine-delay.h>
#include "fdelay-raw.h"
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
struct time_word {
uint32_t val;
char *name;
};
struct time_word words[] = {
{0, "coarse"},
{0, "utc-l"},
{0, "utc-h"}, /* This must be last, as this forces hardware access */
};
int main(int argc, char **argv)
{
char *sysnames[10];
char path[80];
int i, err;
if (argc < 2 || argc > 3) {
fprintf(stderr, "%s: Use \"%s <sec> [<nsec>]\"\n",
argv[0], argv[0]);
exit(1);
}
i = fdelay_get_sysnames(sysnames);
if (!i) {
fprintf(stderr, "%s: no fine-delay devices\n", argv[0]);
exit(1);
}
if (i > 1) {
fprintf(stderr, "%s: several fine-delay devices, using %s\n",
argv[0], sysnames[0]);
}
words[0].val = 0;
words[1].val = atoi(argv[1]); /* FIXME: error check */
words[2].val = 0;
if (argc == 3)
words[0].val = atoi(argv[2]) / 8; /* FIXME: error check */
for (i = 0, err = 0; i < ARRAY_SIZE(words); i++) {
sprintf(path, "%s/%s", sysnames[0], words[i].name);
if (fdelay_sysfs_set(path, &words[i].val) != 0)
err++;
}
if (err) {
fprintf(stderr, "%s: got %i errors writing %zu attributes\n",
argv[0], err, ARRAY_SIZE(words));
}
return 0;
}
#include <stdio.h>
#include <glob.h>
#include <errno.h>
/* return an array of sysfs pathnames, array is preallocated. Returns count */
static inline int fdelay_get_sysnames(char *result[])
{
glob_t glob_buf = {0,};
int i;
glob("/sys/bus/zio/devices/fd-*", 0, NULL, &glob_buf);
glob("/sys/bus/zio/devices/zio-fd-*", GLOB_APPEND , NULL, &glob_buf);
for (i = 0; i < glob_buf.gl_pathc; i++)
result[i] = strdup(glob_buf.gl_pathv[i]);
globfree(&glob_buf);
return i;
}
static inline int fdelay_sysfs_get(char *path, uint32_t *resp)
{
FILE *f = fopen(path, "r");
if (!f)
return -1;
if (fscanf(f, "%i", resp) != 1) {
fclose(f);
errno = EINVAL;
return -1;
}
fclose(f);
return 0;
}
static inline int fdelay_sysfs_set(char *path, uint32_t *value)
{
char s[16];
int fd, ret, len;
len = sprintf(s, "%i\n", *value);
fd = open(path, O_WRONLY);
if (fd < 0)
return -1;
ret = write(fd, s, len);
close(fd);
if (ret < 0)
return -1;
if (ret == len)
return 0;
errno = EINVAL;
return -1;
}
/*
* a simple output tool to make a burst on a parallel port
*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2 as published by the Free Software Foundation or, at your
* option, any later version.
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
/* Returns the numer of microsecond timer ticks (Tomasz Wlostowski) */
static int64_t get_tics()
{
struct timeval tv;
gettimeofday(&tv, NULL);
return (int64_t)tv.tv_sec * 1000 * 1000 + tv.tv_usec;
}
/* Microsecond-accurate delay-to */
static void delay_to(int64_t until)
{
while (get_tics() < until)
;
}
int main(int argc, char **argv)
{
int fd, addr, count, usec;
int64_t tics;
if (argc != 4) {
fprintf(stderr,
"%s: Use \"%s <hexaddr> <count> <period-usec>\"\n",
argv[0], argv[0]);
exit(1);
}
if (sscanf(argv[1], "%x", &addr) != 1) {
fprintf(stderr, "%s: wrong hex \"%s\"\n", argv[0], argv[1]);
exit(1);
}
if (sscanf(argv[2], "%i", &count) != 1) {
fprintf(stderr, "%s: wrong count \"%s\"\n", argv[0], argv[2]);
exit(1);
}
if (sscanf(argv[3], "%i", &usec) != 1) {
fprintf(stderr, "%s: wrong period \"%s\"\n", argv[0], argv[3]);
exit(1);
}
fprintf(stderr, "%s: using port 0x%x, %i pulses, period %i us\n",
argv[0], addr, count, usec);
fd = open("/dev/port", O_RDWR);
if (fd < 0) {
fprintf(stderr, "%s: /dev/port: %s\n", argv[0],
strerror(errno));
exit(1);
}
tics = get_tics();
do {
char b[]={0x00, 0xff};
lseek(fd, addr, SEEK_SET);
write(fd, b + 1, 1);
lseek(fd, addr, SEEK_SET);
write(fd, b + 0, 1);
if (count > 1) {
tics += usec;
delay_to(tics);
}
} while (--count);
return 0;
}
"""
SPDX-License-Identifier: GPL-3.0-or-later
SPDX-FileCopyrightText: 2020 CERN
"""
import pytest
from PyFmcFineDelay import FmcFineDelay
@pytest.fixture(scope="function")
def fmcfd():
fd = FmcFineDelay(pytest.fd_id)
yield fd
for chan in fd.chan:
chan.disable()
def pytest_addoption(parser):
parser.addoption("--fd-id", type=lambda x : int(x, 16),
required=True, help="Fmc Fine-Delay Linux Identifier")
parser.addoption("--channel", type=int, default=[],
action="append", choices=range(FmcFineDelay.CHANNEL_NUMBER),
help="Channel(s) to be used for acquisition tests. Default all channels")
def pytest_configure(config):
pytest.fd_id = config.getoption("--fd-id")
pytest.channels = config.getoption("--channel")
if len(pytest.channels) == 0:
pytest.channels = range(FmcFineDelay.CHANNEL_NUMBER)
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: 2020 CERN
[pytest]
addopts = -v -p no:cacheprovider
\ No newline at end of file
"""
SPDX-License-Identifier: GPL-3.0-or-later
SPDX-FileCopyrightText: 2020 CERN
"""
import pytest
import random
from PyFmcFineDelay import FmcFineDelay, FmcFineDelayTime
@pytest.fixture(scope="function", params=range(0, FmcFineDelay.CHANNEL_NUMBER))
def fmcfd_chan(request):
fd = FmcFineDelay(pytest.fd_id)
yield fd.chan[request.param]
class TestFmcfdGetterSetter(object):
def test_disable(self, fmcfd):
for chan in fmcfd.chan:
chan.disable()
assert chan.disabled == True
@pytest.mark.parametrize("enable", [True, False])
def test_tdc_disable_input(self, fmcfd, enable):
fmcfd.tdc.enable_input = enable
assert fmcfd.tdc.enable_input == enable
@pytest.mark.parametrize("enable", [True, False])
def test_tdc_disable_tstamp(self, fmcfd, enable):
fmcfd.tdc.enable_tstamp = enable
assert fmcfd.tdc.enable_tstamp == enable
@pytest.mark.parametrize("term", [True, False])
def test_tdc_termination(self, fmcfd, term):
"""Set temination and read it back"""
fmcfd.tdc.termination = term
assert term == fmcfd.tdc.termination
@pytest.mark.parametrize("width,period,count", [(FmcFineDelay.FmcFineDelayChannel.OUT_PULSE_DELAY_MIN_WIDTH_PS,
FmcFineDelay.FmcFineDelayChannel.OUT_PULSE_DELAY_MIN_PERIOD_PS,
FmcFineDelay.FmcFineDelayChannel.OUT_PULSE_MAX_COUNT + 1),
(FmcFineDelay.FmcFineDelayChannel.OUT_PULSE_DELAY_MIN_WIDTH_PS - 1,
FmcFineDelay.FmcFineDelayChannel.OUT_PULSE_DELAY_MIN_PERIOD_PS,
1),
(FmcFineDelay.FmcFineDelayChannel.OUT_PULSE_DELAY_MAX_WIDTH_PS + 1,
FmcFineDelay.FmcFineDelayChannel.OUT_PULSE_DELAY_MAX_PERIOD_PS,
1),
(FmcFineDelay.FmcFineDelayChannel.OUT_PULSE_DELAY_MIN_WIDTH_PS,
FmcFineDelay.FmcFineDelayChannel.OUT_PULSE_DELAY_MIN_PERIOD_PS - 1,
1),
(FmcFineDelay.FmcFineDelayChannel.OUT_PULSE_DELAY_MAX_WIDTH_PS,
FmcFineDelay.FmcFineDelayChannel.OUT_PULSE_DELAY_MAX_PERIOD_PS + 1,
1),
])
def test_pulse_delay_invalid(self, fmcfd_chan, width, period, count):
"""The pulse generation can't work with invalid parameters"""
with pytest.raises(OSError):
fmcfd_chan.pulse_delay(FmcFineDelayTime(1, 0, 0),
width, period, count)
@pytest.mark.parametrize("width,period,count", [(FmcFineDelay.FmcFineDelayChannel.OUT_PULSE_DELAY_MIN_WIDTH_PS,
FmcFineDelay.FmcFineDelayChannel.OUT_PULSE_DELAY_MIN_PERIOD_PS,
FmcFineDelay.FmcFineDelayChannel.OUT_PULSE_MAX_COUNT + 1),
(FmcFineDelay.FmcFineDelayChannel.OUT_PULSE_MIN_WIDTH_PS,
FmcFineDelay.FmcFineDelayChannel.OUT_PULSE_MIN_PERIOD_PS,
FmcFineDelay.FmcFineDelayChannel.OUT_PULSE_MAX_COUNT + 1),
(FmcFineDelay.FmcFineDelayChannel.OUT_PULSE_MIN_WIDTH_PS - 1,
FmcFineDelay.FmcFineDelayChannel.OUT_PULSE_MIN_PERIOD_PS,
1),
(FmcFineDelay.FmcFineDelayChannel.OUT_PULSE_MAX_WIDTH_PS + 1,
FmcFineDelay.FmcFineDelayChannel.OUT_PULSE_MAX_PERIOD_PS,
1),
(FmcFineDelay.FmcFineDelayChannel.OUT_PULSE_MIN_WIDTH_PS,
FmcFineDelay.FmcFineDelayChannel.OUT_PULSE_MIN_PERIOD_PS - 1,
1),
(FmcFineDelay.FmcFineDelayChannel.OUT_PULSE_MAX_WIDTH_PS,
FmcFineDelay.FmcFineDelayChannel.OUT_PULSE_MAX_PERIOD_PS + 1,
1),
])
def test_pulse_generate_invalid(self, fmcfd_chan, width, period, count):
"""The pulse generation can't work with invalid parameters"""
with pytest.raises(OSError):
fmcfd_chan.pulse_generate(FmcFineDelayTime(1, 0, 0),
width, period, count)
"""
SPDX-License-Identifier: GPL-3.0-or-later
SPDX-FileCopyrightText: 2020 CERN
"""
import pytest
import select
import time
import os
from PyFmcFineDelay import FmcFineDelay, FmcFineDelayTime
@pytest.fixture(scope="function")
def fmcfd():
fd = FmcFineDelay(pytest.fd_id)
for ch in fd.chan:
ch.disable()
yield fd
for ch in fd.chan:
ch.disable()
@pytest.fixture(scope="function", params=pytest.channels)
def fmcfd_chan(request, fmcfd):
yield fmcfd.chan[request.param]
@pytest.fixture(scope="function")
def fmcfd_tdc(request, fmcfd):
fmcfd.tdc.enable_input = False
fmcfd.tdc.enable_tstamp = False
fmcfd.tdc.termination = False
fmcfd.tdc.flush()
fmcfd.tdc.enable_tstamp = True
fmcfd.tdc.enable_input = True
yield fmcfd.tdc
fmcfd.tdc.enable_input = False
fmcfd.tdc.enable_tstamp = False
def timeout_compute(start, period_ps, count):
proportional = ((count * period_ps)/1000000000000.0)
return time.time() + proportional + start.seconds + 10
class TestFmcfdLoop(object):
"""
The test needs a lemo cable (1ns) that connects all outputs
to the input channel
input
chan o------.
output |
chan 1 o-------+
chan 2 o-------+
chan 3 o-------+
chan 4 o-------`
"""
@pytest.mark.parametrize("count", [10])
@pytest.mark.parametrize("width,period", [(200000, 400000)])
def test_output_flush(self, fmcfd, fmcfd_chan, fmcfd_tdc,
width, period, count):
fmcfd_chan.pulse_generate(fmcfd.time + FmcFineDelayTime(2, 0, 0),
width, period, count)
assert len(fmcfd_tdc.poll(4000)) > 0
fmcfd_tdc.flush()
assert len(fmcfd_tdc.poll(4000)) ==0
@pytest.mark.parametrize("count", [1, 2, 3, 5, 7, 10,
100, 1000, 10000, 65535])
@pytest.mark.parametrize("width,period", [(200000, 10000000000)])
def test_output_counter(self, fmcfd, fmcfd_chan, fmcfd_tdc,
width, period, count):
"""
In pulse mode, the Fine-Delay generates the exact number of
required pulses and we are able to read them all from the input
channel.
"""
ts = []
start = fmcfd.time + FmcFineDelayTime(2, 0, 0)
fmcfd_chan.pulse_generate(start, width, period, count)
timeout = timeout_compute(start, period, count)
while len(ts) < count and time.time() < timeout:
if len(fmcfd_tdc.poll()) == 0:
continue
t = fmcfd_tdc.read(100, os.O_NONBLOCK)
assert len(t) > 0
ts = ts + t
assert len(ts) == count
assert len(fmcfd_tdc.poll(int(period / 1000000000.0))) == 0
del ts
@pytest.mark.parametrize("count", [10000])
@pytest.mark.parametrize("width,period", [(200000, 10000000000)])
def test_input_sequence_number(self, capsys, fmcfd_chan, fmcfd_tdc,
width, period, count):
"""
The input channel has time-stamps with increasing sequence number
with step 1.
"""
pending = count
ts = []
start = FmcFineDelayTime(2, 0, 0)
fmcfd_chan.pulse_generate(fmcfd_chan.dev.time + start,
width, period, count)
timeout = timeout_compute(start, period, count)
while pending > 0 and time.time() < timeout:
if len(fmcfd_tdc.poll()) == 0:
continue
t = fmcfd_tdc.read(pending, os.O_NONBLOCK)
assert len(t) > 0
ts.extend(t)
pending -= len(t)
assert pending == 0
prev_ts = None
for i in range(len(ts)):
if prev_ts is not None:
assert ts[i].seq_id == (prev_ts.seq_id + 1) & 0xFFFF,\
"i:{:d}, cur: {:s}, prev: {:s}".format(i, str(ts[i]),
str(prev_ts))
@pytest.mark.parametrize("start_rel", [FmcFineDelayTime(0, 78125000, 0), # + 0.0625s
FmcFineDelayTime(0, 15625000, 0), # + 0.125s
FmcFineDelayTime(0, 31250000, 0), # + 0.25s
FmcFineDelayTime(0, 62500000, 0), # + 0.5s
FmcFineDelayTime(1, 0, 0), # + 1s
FmcFineDelayTime(1, 78125000, 0), # + 1.0625s
FmcFineDelayTime(1, 15625000, 0), # + 1.125s
FmcFineDelayTime(1, 31250000, 0), # + 1.25s
FmcFineDelayTime(1, 62500000, 0), # + 1.5s
FmcFineDelayTime(2, 0, 0), # + 2s
FmcFineDelayTime(60, 0, 0), # + 60s
FmcFineDelayTime(120, 0, 0), # + 120s
])
@pytest.mark.parametrize("wr", [False, True])
@pytest.mark.parametrize("count", [1])
@pytest.mark.parametrize("width,period", [(200000, 10000000000)])
def test_output_input_start(self, fmcfd_chan, fmcfd_tdc,
wr, start_rel, width, period, count):
"""
The output channel generates a pulse at a given time and the input
channel timestamps it. The two times must be almost the same excluding
the propagation time (cable length).
"""
fmcfd_chan.dev.whiterabbit_mode = wr
ts = []
start = fmcfd_chan.dev.time + start_rel
fmcfd_chan.pulse_generate(start, width, period, count)
assert len(fmcfd_tdc.poll(int(float(start_rel) * 1000) + 2000)) > 0
ts = fmcfd_tdc.read(count, os.O_NONBLOCK)
assert len(ts) == count
assert start.seconds == ts[0].seconds
assert ts[0].coarse - start.coarse <= 1 # there is a ~1ns cable
@pytest.mark.parametrize("width,period_ps", [(200000, 1000000),
(200000, 10000000),
(200000, 100000000),
(200000, 1000000000),
(200000, 10000000000),
(200000, 100000000000),
(200000, 1000000000000),
])
@pytest.mark.parametrize("count", [10])
def test_output_period(self, fmcfd_chan, fmcfd_tdc,
width, period_ps, count):
"""
The test produces pulses on the given channels and catch them using
the on board TDC. The period between two timestamps must be as close
as possible (ideally equal) to the period used to generate them.
"""
ts = []
start = fmcfd_chan.dev.time + FmcFineDelayTime(2, 0, 0, 0, 0)
fmcfd_chan.pulse_generate(start, width, period_ps, count)
time.sleep(2 + (count * period_ps) / 1000000000000.0)
assert len(fmcfd_tdc.poll(10000)) > 0
ts = fmcfd_tdc.read(count, os.O_NONBLOCK)
assert len(ts) == count
prev_ts = None
for i in range(len(ts)):
if prev_ts is not None:
assert ts[i].seq_id == prev_ts.seq_id + 1
period_ts = ts[i] - prev_ts
period = FmcFineDelayTime.from_pico(period_ps)
if period > period_ts:
diff = period - period_ts
else:
diff = period_ts - period
assert diff < FmcFineDelayTime(0, 1, 0), \
"period difference {:s}\n\tcurr: {:s}\n\tprev: {:s}\n\tperi: {:s}".format(str(diff),
str(ts[i]),
str(prev_ts),
str(period_ts))
prev_ts = ts[i]
"""
SPDX-License-Identifier: GPL-3.0-or-later
SPDX-FileCopyrightText: 2020 CERN
"""
import pytest
class TestFmcfdTemperature(object):
def test_temperature_read(self, fmcfd):
assert 0 < fmcfd.temperature
"""
SPDX-License-Identifier: GPL-3.0-or-later
SPDX-FileCopyrightText: 2020 CERN
"""
import pytest
import random
import time
from PyFmcFineDelay import FmcFineDelayTime
class TestFmcfdTime(object):
def test_whiterabbit_mode(self, fmcfd):
"""It must be possible to toggle the White-Rabbit status"""
fmcfd.whiterabbit_mode = True
assert fmcfd.whiterabbit_mode == True
fmcfd.whiterabbit_mode = False
assert fmcfd.whiterabbit_mode == False
def test_time_set_fail_wr(self, fmcfd):
"""Time can't be changed when White-Rabbit is enabled"""
fmcfd.whiterabbit_mode = True
with pytest.raises(OSError):
fmcfd.time = FmcFineDelayTime(10, 0, 0, 0, 0)
@pytest.mark.parametrize("t", random.sample(range(1000000), 10))
def test_time_set(self, fmcfd, t):
"""Time can be changed when White-Rabbit is disabled"""
fmcfd.whiterabbit_mode = False
t_base = FmcFineDelayTime(t, 0, 0, 0, 0)
fmcfd.time = t_base
assert t_base.seconds == fmcfd.time.seconds
@pytest.mark.parametrize("whiterabbit", [False, True])
def test_time_flows(self, fmcfd, whiterabbit):
"""Just check that the time flows more or less correctly second by
second for a minute"""
fmcfd.whiterabbit_mode = whiterabbit
for i in range(20):
t_prev = fmcfd.time.seconds
time.sleep(1)
assert t_prev + 1 == fmcfd.time.seconds
"""
SPDX-License-Identifier: GPL-3.0-or-later
SPDX-FileCopyrightText: 2020 CERN
"""
import pytest
import random
import select
import time
import os
from PyFmcFineDelay import FmcFineDelay, FmcFineDelayTime
@pytest.fixture(scope="function", params=pytest.channels)
def fmcfd_tdc(request):
fd = FmcFineDelay(pytest.fd_id)
fd.tdc.enable_input = False
fd.tdc.enable_tstamp = False
fd.tdc.termination = False
for ch in fd.chan:
ch.disable()
yield fd.tdc
fd.tdc.enable_input = False
fd.tdc.enable_tstamp = False
for ch in fd.chan:
ch.disable()
class TestFmcfdInput(object):
@pytest.mark.parametrize("hz", [1, 10, 100, 1000, 10000])
def test_tdc_burst_manual(self, capsys, fmcfd_tdc, hz):
count = 1000
pending = count
prev_ts = None
fmcfd_tdc.enable_tstamp = True
fmcfd_tdc.enable_input = True
poll = select.poll()
poll.register(fmcfd_tdc.fileno, select.POLLIN)
ts = []
with capsys.disabled():
print("")
print("This test needs an external pulse generator that you manually configure according to the following instructions")
print("1. connect a lemo cable from the pulse generator to the fine-delay trigger input;")
print("2. configure the pulse generator to produce a {:d}Hz burst of {:d} pulses;".format(hz, count))
while True:
a = input("Ready to start? [Y/N]").lower()
if a == 'y' or a == 'n':
break
assert a == "y"
print("### Trigger the burst of pulses and wait for the test to complete (timeout 1 minute)###")
timeout = time.time() + 60
while pending > 0:
t = time.time()
if t >= timeout:
break
ret = poll.poll(1)
if len(ret) == 0:
continue
t = fmcfd_tdc.read(pending, os.O_NONBLOCK)
assert len(t) > 0
ts = ts + t
pending -= len(t)
import pdb
pdb.set_trace()
for i in range(len(ts)):
if prev_ts is not None:
assert ts[i].seq_id == prev_ts.seq_id + 1
diff = float(ts[i]) - float(prev_ts)
assert hz == int(1 / diff)
prev_ts = ts[i]
"""
SPDX-License-Identifier: GPL-3.0-or-later
SPDX-FileCopyrightText: 2020 CERN
"""
import pytest
import random
import time
from PyFmcFineDelay import FmcFineDelay, FmcFineDelayTime
@pytest.fixture(scope="function", params=pytest.channels)
def fmcfd_chan(request):
fd = FmcFineDelay(pytest.fd_id)
fd.tdc.enable_input = False
fd.tdc.enable_tstamp = False
fd.tdc.termination = False
fd.chan[request.param].disable()
yield fd.chan[request.param]
fd.chan[request.param].disable()
class TestFmcfdOutput(object):
def __print_configuration(self, chan, delay, width, period, count):
print("configuration")
print(" channel: {:d}".format(chan))
print(" delay : {:d}ps".format(delay))
print(" width : {:d}ps".format(width))
print(" period : {:d}ps".format(period))
print(" count : {:d}".format(count))
@pytest.mark.parametrize("width",[50000, # 50ns
60000, # 60ns
70000, # 70ns
80000, # 80ns
90000, # 90ns
500000, # 500ns
1000000, # 1us
500000000, # 500us
1000000000, # 1ms
500000000000, # 500ms
1000000000000, # 1s
])
@pytest.mark.parametrize("count", [1, 3])
def test_pulse_width(self, capsys, fmcfd_chan, width, count):
with capsys.disabled():
print("")
print("This test needs an external oscilloscope to monitor pulses produced by the fine delay")
print("1. connect a lemo cable from the fine-delay channel {:d} to the oscilloscope;".format(fmcfd_chan.idx + 1))
print("2. set up the oscilloscope to catch the pulse(s)")
input("Press any key to start").lower()
while True:
start = fmcfd_chan.dev.time + FmcFineDelayTime(1, 0, 0)
fmcfd_chan.pulse_generate(start, width, width * 2, count)
ret = self.__process_outcome(fmcfd_chan.idx + 1, 0,
width, width * 2, count)
if ret in ["y", "n", "q"]:
break
if ret == "q":
pytest.skip("Quit test")
assert ret == "y"
@pytest.mark.parametrize("delay", [600000, # 600ns
1000000, # 1us
500000000, # 500us
1000000000, # 1ms
500000000000, # 500ms
1000000000000, # 1s
10000000000000, # 10s
120000000000000 # 120s
])
def test_pulse_delay_start(self, capsys, fmcfd_chan, delay):
fmcfd_chan.dev.tdc.enable_input = True
with capsys.disabled():
print("")
print("For this test you need: a pulse-generator, an oscilloscope")
print("1. connect a lemo cable from the fine-delay channel {:d} to the oscilloscope;".format(fmcfd_chan.idx + 1))
print("2. connect a lemo cable from the pulse generator to the fine-delay trigger input;")
print("3. connect a lemo cable from the pulse generator to the oscilloscope;")
print("4. Configure the pulse generator at 1Hz")
print("5. set up the oscilloscope to catch the pulse(s)")
input("Press any key to start").lower()
while True:
fmcfd_chan.pulse_delay(delay, 250000, 500000, 1)
time.sleep(1 + delay/1000000000000.0)
ret = self.__process_outcome(fmcfd_chan.idx + 1, delay, 250000, 500000, 1)
if ret in ["y", "n", "q"]:
break
if ret == "q":
pytest.skip("Quit test")
assert ret == "y"
@pytest.mark.parametrize("width",[250000, # 250ns
260000, # 260ns
270000, # 270ns
280000, # 280ns
290000, # 290ns
500000, # 500ns
1000000, # 1us
500000000, # 500us
1000000000, # 1ms
500000000000, # 500ms
1000000000000, # 1s
])
@pytest.mark.parametrize("count", [1, 3])
def test_pulse_delay_width(self, capsys, fmcfd_chan, width, count):
fmcfd_chan.dev.tdc.enable_input = True
with capsys.disabled():
print("")
print("For this test you need: a pulse-generator, an oscilloscope")
print("1. connect a lemo cable from the fine-delay channel {:d} to the oscilloscope;".format(fmcfd_chan.idx + 1))
print("2. connect a lemo cable from the pulse generator to the fine-delay trigger input;")
print("3. connect a lemo cable from the pulse generator to the oscilloscope;")
print("4. Configure the pulse generator at 1Hz")
print("5. set up the oscilloscope to catch the pulse(s)")
input("Press any key to start").lower()
while True:
fmcfd_chan.pulse_delay(width, width, 500000, 1)
time.sleep(1 + delay/1000000000000.0)
ret = self.__process_outcome(fmcfd_chan.idx + 1, 600000, width, 500000, 1)
if ret in ["y", "n", "q"]:
break
if ret == "q":
pytest.skip("Quit test")
assert ret == "y"
def __process_outcome(chan, start, width, period, count):
while True:
with capsys.disabled():
self.__print_configuration(chan, start, width, period, count)
a = input("Did you see it on the oscilloscope? [Y/N/R/Q]").lower()[0]
if a in ["y", "n", "r", "q"]:
break
return a
#!/bin/bash
if [ $# -gt 2 -o $# -lt 1 ]; then
echo "Use: \"$0 <repo-name> <user-provided-path>\" >& 2"
echo "Example: \"$0 spec-sw $SPEC_SW\" >& 2"
exit 1
fi
repo_name=$1
repo_path=$2
# User-provided takes precedence
if [ "$repo_path" != "" ]; then
echo "Submodule \"$repo_name\": using provided path \"$repo_path\"" >&2
echo $(readlink -f $repo_path)
exit 0
fi
# If this project and the other project are both submodules, pick ../<repo>
# Otherwise, pick subdirectory of current project
if [ ! -f ../.gitmodules ]; then
# dot-dot has no submodules, pick subdir
echo "Submodule \"$repo_name\": using local copy" >&2
echo $(readlink -f $repo_name)
exit 0
fi
this_dir=$(basename $(/bin/pwd))
if ! grep "submodule \"$this_dir\"" ../.gitmodules > /dev/null; then
# we are not a submodule of dot-dot
echo "Submodule \"$repo_name\": using local copy" >&2
echo $(readlink -f $repo_name)
exit 0
fi
if ! grep "submodule \"$repo_name\"" ../.gitmodules > /dev/null; then
# our repo is not a submodule of dot-dot
echo $FMC_SUBDIR
echo "Submodule \"$repo_name\": using local copy" >&2
echo $(readlink -f $repo_name)
exit 0
fi
echo "Submodule \"$repo_name\": using copy in parent directory" >&2
echo $(readlink -f ../$repo_name)
exit 0
GW_SPEC = spec-fine-delay-v2.0-20140331.bin
GW_SVEC = svec-fine-delay-v2.0-20140331.bin
GW_URL_SPEC = http://www.ohwr.org/attachments/download/2777/$(GW_SPEC)
GW_URL_SVEC = http://www.ohwr.org/attachments/download/2778/$(GW_SVEC)
FIRMWARE_PATH ?= /lib/firmware/fmc
gateware_install: bin/$(GW_SPEC) bin/$(GW_SVEC)
install -D bin/$(GW_SPEC) $(FIRMWARE_PATH)/$(GW_SPEC)
install -D bin/$(GW_SVEC) $(FIRMWARE_PATH)/$(GW_SVEC)
ln -sf $(GW_SPEC) $(FIRMWARE_PATH)/spec-fine-delay.bin
ln -sf $(GW_SVEC) $(FIRMWARE_PATH)/svec-fine-delay.bin
bin/$(GW_SPEC):
wget $(GW_URL_SPEC) -P bin
bin/$(GW_SVEC):
wget $(GW_URL_SVEC) -P bin
fmc-fdelay-list
fmc-fdelay-term
fmc-fdelay-board-time
fmc-fdelay-input
fmc-fdelay-pulse
fmc-fdelay-status
fmc-fdelay-calibration
# user-space tools for spec-fine-delay
# If it exists includes Makefile.specific. In this Makefile, you should put
# specific Makefile code that you want to run before this. For example,
# build a particular environment.
-include Makefile.specific
# include parent_common.mk for buildsystem's defines
REPO_PARENT=../..
-include $(REPO_PARENT)/parent_common.mk
DESTDIR ?= /usr/local
GIT_VERSION := $(shell git describe --dirty --long --tags)
LIBFD := ../lib
CFLAGS += -I../kernel -I$(LIBFD) -Wno-trigraphs -Wall -Werror -ggdb $(EXTRACFLAGS)
CFLAGS += -DGIT_VERSION="\"$(GIT_VERSION)\""
LDFLAGS = -L$(LIBFD)
LDLIBS = -lfdelay
CC ?= $(CROSS_COMPILE)gcc
progs := fmc-fdelay-list
progs += fmc-fdelay-term
progs += fmc-fdelay-board-time
progs += fmc-fdelay-input
progs += fmc-fdelay-pulse
progs += fmc-fdelay-status
CPPCHECK ?= cppcheck
# we are not in the kernel, so we need to piggy-back on "make modules"
all modules: $(progs) fmc-fdelay-calibration
clean:
rm -f $(progs) *.o *~
COMMON_SRCS = tools-util.c
$(progs): $(COMMON_SRCS:.c=.o)
fmc-fdelay-calibration:
# make nothing for modules_install, but avoid errors
modules_install:
install:
install -d $(DESTDIR)/bin
install -D $(progs) $(DESTDIR)/bin
cppcheck:
$(CPPCHECK) -q -I. -I../kernel -I$(LIBFD) --suppress=missingIncludeSystem --enable=all *.c *.h
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include "fdelay-lib.h"
#include "tools-common.h"
char git_version[] = "git version: " GIT_VERSION;
void help(char *name)
{
fprintf(stderr, "fmc-fdelay-board-time: a tool for manipulating the FMC Fine Delay time base.\n");
fprintf(stderr, "Use: \"%s [-V] [-d <dev>] <command>\"\n",
name);
fprintf(stderr, " where the <command> can be:\n"
" get - shows current time and White Rabbit status.\n"
" local - sets the time source to the card's local oscillator.\n"
" wr - sets the time source to White Rabbit.\n"
" host - sets the time source to local oscillator and coarsely\n"
" synchronizes the card to the system clock.\n"
" seconds:[nanoseconds]: - sets local time to the given value.\n");
exit(1);
}
int main(int argc, char **argv)
{
struct fdelay_board *b;
struct fdelay_time t;
int i, get = 0, host = 0, wr_on = 0, wr_off = 0;
int dev = -1, err;
char *s;
/* Standard part of the file (repeated code) */
if (tools_need_help(argc, argv))
help(argv[0]);
/* print versions if needed */
print_version(argc, argv);
err = fdelay_init();
if (err) {
fprintf(stderr, "%s: library initialization failed\n", argv[0]);
exit(1);
}
tools_getopt_d_i(argc, argv, &dev);
if (dev < 0) {
fprintf(stderr, "%s: several boards, please pass -d\n",
argv[0]);
exit(1);
}
/* Parse the mandatory extra argument */
if (optind != argc - 1)
help(argv[0]);
s = argv[optind];
/* Crappy parser */
if (!strcmp(s, "get"))
get = 1;
else if (!strcmp(s, "host"))
host = 1;
else if (!strcmp(s, "wr"))
wr_on = 1;
else if (!strcmp(s, "local"))
wr_off = 1;
else {
double nano;
long long sec;
memset(&t, 0, sizeof(t));
i = sscanf(s, "%lli%lf\n", &sec, &nano);
if (i < 1) {
fprintf(stderr, "%s: Not a number \"%s\"\n",
argv[0], s);
exit(1);
}
t.utc = sec;
t.coarse = nano * 1000 * 1000 * 1000 / 8;
}
b = fdelay_open(dev);
if (!b) {
fprintf(stderr, "%s: fdelay_open(): %s\n", argv[0],
strerror(errno));
exit(1);
}
if (get) {
if (fdelay_get_time(b, &t) < 0) {
fprintf(stderr, "%s: fdelay_get_time(): %s\n", argv[0],
strerror(errno));
exit(1);
}
err = fdelay_check_wr_mode(b);
printf("WR Status: ");
switch(err)
{
case ENODEV: printf("disabled.\n"); break;
case ENOLINK: printf("link down.\n"); break;
case EAGAIN: printf("synchronization in progress.\n"); break;
case 0: printf("synchronized.\n"); break;
default: printf("error: %s\n", strerror(errno)); break;
}
printf("Time: %lli.%09li\n", (long long)t.utc, (long)t.coarse * 8);
fdelay_close(b);
fdelay_exit();
return 0;
}
if (host) {
if (fdelay_set_host_time(b) < 0) {
fprintf(stderr, "%s: fdelay_set_host_time(): %s\n",
argv[0], strerror(errno));
exit(1);
}
fdelay_close(b);
fdelay_exit();
return 0;
}
if (wr_on) {
setbuf(stdout, NULL);
printf("Locking the card to WR: ");
err = fdelay_wr_mode(b, 1);
if(err == ENOTSUP)
{
fprintf(stderr, "%s: no support for White Rabbit (check the gateware).\n",
argv[0]);
exit(1);
} else if (err) {
fprintf(stderr, "%s: fdelay_wr_mode(): %s\n",
argv[0], strerror(errno));
exit(1);
}
while ((err = fdelay_check_wr_mode(b)) != 0) {
if( err == ENOLINK )
{
fprintf(stderr, "%s: no White Rabbit link (check the cable and the switch).\n",
argv[0]);
exit(1);
}
printf(".");
sleep(1);
}
printf(" locked!\n");
fdelay_close(b);
fdelay_exit();
return 0;
}
if (wr_off) {
if (fdelay_wr_mode(b, 0) < 0) {
fprintf(stderr, "%s: fdelay_wr_mode(): %s\n",
argv[0], strerror(errno));
exit(1);
}
fdelay_close(b);
fdelay_exit();
return 0;
}
if (fdelay_set_time(b, &t) < 0) {
fprintf(stderr, "%s: fdelay_set_time(): %s\n",
argv[0], strerror(errno));
exit(1);
}
fdelay_close(b);
fdelay_exit();
return 0;
}
// SPDX-License-Identifier: GPL-3.0-or-later
/*
* Copyright (C) 2019 CERN (www.cern.ch)
* Author: Federico Vaga <federico.vaga@cern.ch>
*/
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <inttypes.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <endian.h>
#include <fine-delay.h>
static const char program_name[] = "fau-calibration";
static char options[] = "hf:o:D:b";
static const char help_msg[] =
"Usage: fmc_fdelay_-calibration [options]\n"
"\n"
"It reads calibration data from a file that contains it in binary\n"
"form and it shows it on STDOUT in binary form or in human readable\n"
"one (default).\n"
"This could be used to change the FDelay calibration data at runtime\n"
"by redirectiong the binary output of this program to the proper \n"
"sysfs binary attribute\n"
"Rembember that we expect all values to be big endian\n"
"\n"
"General options:\n"
"-h Print this message\n"
"-b Show Calibration in binary form \n"
"\n"
"Read options:\n"
"-f Source file where to read calibration data from\n"
"-o Offset in bytes within the file (default 0)\n"
"Write options:\n"
"-D FMC FDelay Target Device ID\n"
"\n";
/**
* Read calibration data from file
* @path: file path
* @calib: calibration data
* @offset: offset in file
*
* Return: number of bytes read
*/
static int fmc_fdelay_calibration_read(char *path,
struct fd_calibration *calib,
off_t offset)
{
int fd;
int ret = 0;
fd = open(path, O_RDONLY);
if (fd < 0)
return -1;
ret = lseek(fd, offset, SEEK_SET);
if (ret >= 0)
ret = read(fd, calib, sizeof(*calib));
close(fd);
return ret;
}
/**
* Print calibration data on stdout in humand readable format
* @calib: calibration data
*/
static void fmc_fdelay_calibration_dump_human(struct fd_calibration *calib)
{
int i;
/* Fix Endianess */
calib->magic = be32toh(calib->magic);
calib->hash = be32toh(calib->hash);
calib->size = be16toh(calib->size);
calib->version = be16toh(calib->version);
calib->date = be32toh(calib->date);
for (i = 0; i < 3; ++i)
calib->frr_poly[i] = be64toh(calib->frr_poly[i]);
for (i = 0; i < 4; ++i)
calib->zero_offset[i] = be32toh(calib->zero_offset[i]);
calib->tdc_zero_offset = be32toh(calib->tdc_zero_offset);
calib->vcxo_default_tune = be32toh(calib->vcxo_default_tune);
fprintf(stdout, "Signature : 0x%08"PRIx32"\n", calib->magic);
fprintf(stdout, "Hash : 0x%08"PRIx32"\n", calib->hash);
fprintf(stdout, "Size : %08"PRId16"\n", calib->size);
fprintf(stdout, "Version : %08"PRId16"\n", calib->version);
fprintf(stdout, "Date : 0x%08"PRIx32"\n", calib->date);
for (i =0; i < 3; ++i)
fprintf(stdout, "FRR-poly[%d] : 0x%016"PRIx64"\n", i,
calib->frr_poly[i]);
for (i =0; i < 4; ++i)
fprintf(stdout, "zero-offset[%d] : 0x%016"PRIx32"\n", i,
calib->zero_offset[i]);
fprintf(stdout, "TDC-zero-offset : 0x%08"PRIx32"\n", calib->tdc_zero_offset);
fprintf(stdout, "VCXO tune : 0x%08"PRIx32"\n",
calib->vcxo_default_tune);
fputc('\n', stdout);
}
/**
* Print binary calibration data on stdout
* @calib: calibration data
*/
static void fmc_fdelay_calibration_dump_machine(struct fd_calibration *calib)
{
write(fileno(stdout), calib, sizeof(*calib));
}
/**
* Write calibration data to device
* @devid: Device ID
* @calib: calibration data
*
* Return: number of bytes wrote
*/
static int fmc_fdelay_calibration_write(unsigned int devid, struct fd_calibration *calib)
{
char path[128];
int fd;
int ret;
sprintf(path,
"/sys/bus/zio/devices/fd-%04x/calibration_data",
devid);
fd = open(path, O_WRONLY);
if (fd < 0)
return -1;
ret = write(fd, calib, sizeof(*calib));
close(fd);
return ret;
}
int main(int argc, char *argv[])
{
char c;
int ret;
char *path = NULL;
unsigned int offset = 0;
unsigned int devid = 0;
int show_bin = 0, write = 0;
struct fd_calibration calib;
while ((c = getopt(argc, argv, options)) != -1) {
switch (c) {
default:
case 'h':
fprintf(stderr, help_msg);
exit(EXIT_SUCCESS);
case 'D':
ret = sscanf(optarg, "0x%x", &devid);
if (ret != 1) {
fprintf(stderr,
"Invalid devid %s\n",
optarg);
exit(EXIT_FAILURE);
}
write = 1;
break;
case 'f':
path = optarg;
break;
case 'o':
ret = sscanf(optarg, "0x%x", &offset);
if (ret != 1) {
ret = sscanf(optarg, "%u", &offset);
if (ret != 1) {
fprintf(stderr,
"Invalid offset %s\n",
optarg);
exit(EXIT_FAILURE);
}
}
break;
case 'b':
show_bin = 1;
break;
}
}
if (!path) {
fputs("Calibration file is mandatory\n", stderr);
exit(EXIT_FAILURE);
}
/* Read EEPROM file */
ret = fmc_fdelay_calibration_read(path, &calib, offset);
if (ret < 0) {
fprintf(stderr, "Can't read calibration data from '%s'. %s\n",
path, strerror(errno));
exit(EXIT_FAILURE);
}
if (ret != sizeof(calib)) {
fprintf(stderr,
"Can't read all calibration data from '%s'. %s\n",
path, strerror(errno));
exit(EXIT_FAILURE);
}
/* Show calibration data*/
if (show_bin)
fmc_fdelay_calibration_dump_machine(&calib);
else if(!write)
fmc_fdelay_calibration_dump_human(&calib);
/* Write calibration data */
if (write) {
ret = fmc_fdelay_calibration_write(devid, &calib);
if (ret < 0) {
fprintf(stderr,
"Can't write calibration data to '0x%x'. %s\n",
devid, strerror(errno));
exit(EXIT_FAILURE);
}
if (ret != sizeof(calib)) {
fprintf(stderr,
"Can't write all calibration data to '0x%x'. %s\n",
devid, strerror(errno));
exit(EXIT_FAILURE);
}
}
exit(EXIT_SUCCESS);
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "fdelay-lib.h"
#include "tools-common.h"
char git_version[] = "git version: " GIT_VERSION;
void help(char *name)
{
fprintf(stderr, "%s: Use \"%s [-V] [-d <dev>] [<opts>]\n",
name, name);
fprintf(stderr, " options:\n"
" -c <count> default is 0 and means forever\n"
" -n nonblocking: only empty buffer\n"
" -r raw mode: show hex timestamps\n"
" -f floating point (default): sec.nsec\n");
exit(1);
}
void dump_input(struct fdelay_time *t, int np, int umode)
{
int i;
for (i = 0; i < np; i++, t++) {
printf("seq %5u: ", t->seq_id);
tools_report_time("", t, umode);
}
}
int main(int argc, char **argv)
{
struct fdelay_board *b;
int opt, err, dev = -1;
int nonblock = 0, count = 0;
int umode = TOOLS_UMODE_USER, flags;
/* Standard part of the file (repeated code) */
if (tools_need_help(argc, argv))
help(argv[0]);
/* print versions if needed */
print_version(argc, argv);
err = fdelay_init();
if (err) {
fprintf(stderr, "%s: library initialization failed\n", argv[0]);
exit(1);
}
/* Parse our specific arguments, starting back from argv[1] */
while ((opt = getopt(argc, argv, "d:hc:nrf")) != -1) {
switch (opt) {
char *rest;
case 'd':
dev = strtol(optarg, &rest, 0);
if (rest && *rest) {
fprintf(stderr, "%s: Not a number \"%s\"\n",
argv[0], optarg);
exit(1);
}
break;
case 'h':
help(argv[0]);
case 'c':
count = strtol(optarg, &rest, 0);
if (rest && *rest) {
fprintf(stderr, "%s: Not a number \"%s\"\n",
argv[0], optarg);
exit(1);
}
break;
case 'n':
nonblock = O_NONBLOCK;
break;
case 'r':
umode = TOOLS_UMODE_RAW;
break;
case 'f':
umode = TOOLS_UMODE_FLOAT;
break;
}
}
if (optind != argc)
help(argv[0]); /* too many arguments */
if (dev < 0) {
fprintf(stderr, "%s: several boards, please pass -d\n",
argv[0]);
exit(1);
}
b = fdelay_open(dev);
if (!b) {
fprintf(stderr, "%s: fdelay_open(): %s\n", argv[0],
strerror(errno));
exit(1);
}
flags = fdelay_get_config_tdc(b);
if (flags < 0) {
err = flags;
goto out;
}
flags &= ~(FD_TDCF_DISABLE_INPUT | FD_TDCF_DISABLE_TSTAMP);
err = fdelay_set_config_tdc(b, flags);
if (err) {
fprintf(stderr, "%s: failed to configure TDC: %s\n", argv[0],
fdelay_strerror(errno));
goto out;
}
/* now read pulses, "np" at a time */
while (1) {
struct fdelay_time pdata[16];
int ret, np = 16;
if (count && count < np)
np = count;
ret = fdelay_read(b, pdata, np, nonblock);
if (ret < 0) {
err = ret;
fprintf(stderr, "%s: fdelay_read: %s\n", argv[0],
strerror(errno));
break;
}
if (!ret)
continue;
dump_input(pdata, ret, umode);
if (nonblock) /* non blocking: nothing more to do */
break;
if (!count) /* no count: forever */
continue;
count -= ret;
if (!count) /* asked that many, we are done */
break;
}
out:
fdelay_close(b);
fdelay_exit();
return err;
}
/* Silly thing that lists installed fine-delay boards */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <glob.h>
#include "fdelay-lib.h"
#include "tools-common.h"
char git_version[] = "git version: " GIT_VERSION;
void help(char *name)
{
fprintf(stderr, "%s: Lists boards\n"
" -V print version\n", name);
exit(1);
}
int main(int argc, char **argv)
{
glob_t g;
int err, i;
if (tools_need_help(argc, argv))
help(argv[0]);
/* print versions if needed */
print_version(argc, argv);
if (argc > 1) {
fprintf(stderr, "%s: too many arguments (none expected)\n",
argv[0]);
exit(1);
}
err = fdelay_init();
if (err) {
fprintf(stderr, "%s: library initialization failed\n",
argv[0]);
exit(1);
}
err = glob("/dev/zio/fd-[A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9]-0-0-ctrl",
GLOB_NOSORT, NULL, &g);
if (err == GLOB_NOMATCH)
goto out_glob;
for (i = 0; i < g.gl_pathc; i++) {
uint32_t dev_id;
char dev_id_str[7]= "0x";
/* Keep only the ID */
strncpy(dev_id_str + 2,
g.gl_pathv[i] + strlen("/dev/zio/fd-"), 4);
dev_id = strtol(dev_id_str, NULL, 0);
printf(" Fine-Delay Device ID %04x\n", dev_id);
}
globfree(&g);
out_glob:
fdelay_exit();
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <inttypes.h>
#include "fdelay-lib.h"
#include "tools-common.h"
char git_version[] = "git version: " GIT_VERSION;
void help(char *name)
{
fprintf(stderr, "%s: Use \"%s [-V] [-d <dev>] [<opts>]\n",
name, name);
fprintf(stderr, " options:\n"
" -o <output> ouput channel: 1..4 (default 1)\n"
" -c <count> default is 0 and means forever\n"
" -m <mode> \"pulse\" (default), \"delay\", \"disable\"\n"
" -r <reltime> relative time, e.g. \"10m+20u\" -- use m,u,n,p and add/sub\n"
" -D <date> absolute time, <secs>:<nano>\n"
" -T <period> period, e.g. \"50m-20n\" -- use m,u,n,p and add/sub\n"
" -w <width> like period; defaults to 50%% period\n"
" -t wait for trigger before exiting\n"
" -p pulse per seconds (sets -D -T -w)\n"
" -1 10MHz (sets -D -T -w)\n"
" -v verbose (report action)\n");
fprintf(stderr,"By default, the tool generates a continuous train of pulses (10 Hz frequency) on a given output.\n");
exit(1);
}
struct fdelay_time t_width; /* save width here, add to start before acting */
/* This comes from oldtools/fdelay-pulse-tom.c, unchanged */
static void parse_time(char *s, struct fdelay_time *t)
{
int64_t time_ps = 0;
int64_t extra_seconds = 0;
int64_t sign = 1;
int64_t term = 0;
int64_t scale = 1;
const int64_t one_second = 1000000000000LL;
char c, *buf = s;
while ((c = *buf++) != 0) {
switch (c) {
case '+':
if (scale == one_second)
extra_seconds += sign * term;
else
time_ps += sign * term * scale;
term = 0;
sign = 1;
break;
case '-':
if (scale == one_second)
extra_seconds += sign * term;
else
time_ps += sign * term * scale;
term = 0;
sign = -1;
break;
case 's':
scale = one_second;
break;
case 'm':
scale = 1000000000LL;
break;
case 'u':
scale = 1000000LL;
break;
case 'n':
scale = 1000LL;
break;
case 'p':
scale = 1LL;
break;
default:
if (isdigit(c)) {
term *= 10LL;
term += (int64_t) (c - '0');
break;
} else {
fprintf(stderr,
"Error while parsing time string '%s'\n",
s);
exit(-1);
}
}
}
if (scale == one_second)
extra_seconds += sign * term;
else
time_ps += sign * term * scale;
while (time_ps < 0) {
time_ps += one_second;
extra_seconds--;
}
fdelay_pico_to_time((uint64_t *) & time_ps, t);
t->utc += extra_seconds;
if (0)
printf("dbg: raw %"PRId64", %"PRId64", converted: %"PRIu64" s %"PRId32" ns %"PRId32" ps\n",
extra_seconds, time_ps, t->utc, t->coarse * 8,
t->frac * 8000 / 4096);
}
/* This comes from oldtools/fdelay-pulse-tom.c, unchanged */
static struct fdelay_time ts_add(struct fdelay_time a, struct fdelay_time b)
{
a.frac += b.frac;
if (a.frac >= 4096) {
a.frac -= 4096;
a.coarse++;
}
a.coarse += b.coarse;
if (a.coarse >= 125000000) {
a.coarse -= 125000000;
a.utc++;
}
a.utc += b.utc;
return a;
}
/*
* Some argument parsing is non-trivial, including setting
* the default. These helpers just return void and exit on error
*/
#define COARSE_PER_SEC (125 * 1000 * 1000)
void parse_default(struct fdelay_pulse *p)
{
memset(p, 0, sizeof(*p));
memset(&t_width, 0, sizeof(struct fdelay_time));
p->mode = FD_OUT_MODE_PULSE;
p->rep = -1; /* 1 pulse */
p->start.utc = 0;
/* Default settings are for 10Hz, 1us width */
p->loop.coarse = COARSE_PER_SEC / 10;
t_width.coarse = 125;
}
void parse_pps(struct fdelay_pulse *p)
{
parse_default(p);
t_width.coarse = COARSE_PER_SEC / 100; /* 10ms width */
p->loop.coarse = 0;
p->loop.utc = 1;
}
void parse_10mhz(struct fdelay_pulse *p)
{
parse_default(p);
t_width.coarse = 6 /* 48ns */;
p->loop.coarse = 12 /* 96ns */;
p->loop.frac = 2048 /* 4ns */;
}
void parse_reltime(struct fdelay_pulse *p, char *s)
{
memset(&p->start, 0, sizeof(p->start));
parse_time(s, &p->start);
}
void parse_abstime(struct fdelay_pulse *p, char *s)
{
unsigned long long utc;
unsigned long nanos;
char c;
if (sscanf(s, "%llu:%lu%c", &utc, &nanos, &c) != 2) {
fprintf(stderr, "Wrong <sec>:<nano> string \"%s\"\n", s);
exit(1);
}
p->start.utc = utc;
p->start.coarse = nanos / 8;
p->start.frac = (nanos % 8) * 512;
}
void parse_period(struct fdelay_pulse *p, char *s)
{
memset(&p->loop, 0, sizeof(p->loop));
parse_time(s, &p->loop);
}
void parse_width(struct fdelay_pulse *p, char *s)
{
memset(&t_width, 0, sizeof(struct fdelay_time));
parse_time(s, &t_width);
}
int main(int argc, char **argv)
{
struct fdelay_board *b;
int i, opt, dev = -1, err = 0, flags;
/* our parameters */
int count = 0, channel = -1;
int trigger_wait = 0, verbose = 0;
struct fdelay_pulse p;
/* Standard part of the file (repeated code) */
if (tools_need_help(argc, argv))
help(argv[0]);
/* print versions if needed */
print_version(argc, argv);
err = fdelay_init();
if (err) {
fprintf(stderr, "%s: library initialization failed\n", argv[0]);
exit(1);
}
parse_default(&p);
/* Parse our specific arguments */
while ((opt = getopt(argc, argv, "d:ho:c:m:r:D:T:w:tp1v")) != -1) {
switch (opt) {
char *rest;
case 'd':
dev = strtol(optarg, &rest, 0);
if (rest && *rest) {
fprintf(stderr, "%s: Not a number \"%s\"\n",
argv[0], optarg);
exit(1);
}
break;
case 'h':
help(argv[0]);
case 'o':
channel = strtol(optarg, &rest, 0);
if (rest && *rest) {
fprintf(stderr, "%s: Not a number \"%s\"\n",
argv[0], optarg);
exit(1);
}
if (channel < 1 || channel > 4) {
fprintf(stderr, "%s: channel \"%s\" out of range\n",
argv[0], optarg);
exit(1);
}
break;
case 'c':
count = strtol(optarg, &rest, 0);
if (rest && *rest) {
fprintf(stderr, "%s: Not a number \"%s\"\n",
argv[0], optarg);
exit(1);
}
p.rep = count ? count : -1 /* infinite */;
break;
case 'm':
if (!strcmp(optarg, "disable"))
p.mode = FD_OUT_MODE_DISABLED;
else if (!strcmp(optarg, "pulse"))
p.mode = FD_OUT_MODE_PULSE;
else if (!strcmp(optarg, "delay"))
p.mode = FD_OUT_MODE_DELAY;
else {
fprintf(stderr, "%s: invalid mode \"%s\"\n",
argv[0], optarg);
exit(1);
}
break;
case 'r':
parse_reltime(&p, optarg);
break;
case 'D':
parse_abstime(&p, optarg);
break;
#if 0 /* no frequency */
case 'f':
parse_freq(&p, optarg);
break;
#endif
case 'T':
parse_period(&p, optarg);
break;
case 'w':
parse_width(&p, optarg);
break;
case 't':
trigger_wait = 1;
break;
case 'p':
parse_pps(&p);
break;
case '1':
parse_10mhz(&p);
break;
case 'v':
verbose = 1;
break;
}
}
if (optind != argc)
help(argv[0]); /* too many arguments */
if (dev < 0) {
fprintf(stderr, "%s: several boards, please pass -d\n",
argv[0]);
exit(1);
}
b = fdelay_open(dev);
if (!b) {
fprintf(stderr, "%s: fdelay_open(): %s\n", argv[0],
strerror(errno));
exit(1);
}
if(channel < 0)
{
fprintf(stderr, "%s: no channel specified (-o option missing).\n", argv[0]);
exit(1);
}
/* Final fixes: if reltime in pulse mode, add current time */
if (p.mode == FD_OUT_MODE_PULSE && p.start.utc == 0) {
struct fdelay_time current_board_time;
fdelay_get_time(b, &current_board_time);
/* Start next second, or next again if too near to overlap */
p.start.utc = current_board_time.utc + 1;
if (current_board_time.coarse > COARSE_PER_SEC * 9 / 10)
p.start.utc++;
}
/* Report to user how parsing turned out to be */
if(verbose)
{
printf("Parsed times:\n");
tools_report_time(" start time: ", &p.start, TOOLS_UMODE_USER);
tools_report_time(" pulse width:", &t_width, TOOLS_UMODE_USER);
tools_report_time(" period: ", &p.loop, TOOLS_UMODE_USER);
}
/* End is start + width, in every situation */
p.end = ts_add(p.start, t_width);
/* In delay mode, default is one pulse only; recover if wrong
The same rule apply also for the disabled mode */
if ((p.mode == FD_OUT_MODE_DELAY || p.mode == FD_OUT_MODE_DISABLED) &&
p.rep <= 0)
p.rep = 1;
/* In delay mode, the minimum delay is 600ns; recover if wrong
The same rule apply also for the disabled mode */
if ((p.mode == FD_OUT_MODE_DELAY || p.mode == FD_OUT_MODE_DISABLED) &&
p.start.utc == 0 && p.start.coarse < (600 / 8)) {
p.start.coarse = 600 / 8;
p.start.frac = 0;
}
/* Done. Report verbosely and activate the information we parsed */
channel = FDELAY_OUTPUT_USER_TO_HW(channel);
report_output_config(channel, &p, TOOLS_UMODE_USER);
flags = fdelay_get_config_tdc(b);
if (flags < 0) {
err = flags;
goto out;
}
flags &= ~FD_TDCF_DISABLE_INPUT;
err = fdelay_set_config_tdc(b, flags);
if (err) {
fprintf(stderr, "%s: failed to configure TDC: %s\n", argv[0],
fdelay_strerror(errno));
goto out;
}
if ((err = fdelay_config_pulse(b, channel, &p)) < 0) {
fprintf(stderr, "%s: fdelay_config_pulse(): %s\n",
argv[0], strerror(-err));
exit(1);
}
while (trigger_wait) {
usleep(10 * 1000);
i = fdelay_has_triggered(b, channel);
if (i < 0) {
fprintf(stderr, "%s: waiting for trigger: %s\n",
argv[0], strerror(errno));
exit(1);
}
trigger_wait = !i;
}
out:
fdelay_close(b);
fdelay_exit();
return err;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include "fdelay-lib.h"
#include "tools-common.h"
char git_version[] = "git version: " GIT_VERSION;
void help(char *name)
{
fprintf(stderr, "fmc-fdelay-status: reports channel programming\n");
fprintf(stderr, "Use: \"%s [-V] [-d <dev>] [-r]\"\n", name);
fprintf(stderr, " -r: display raw hardware configuration");
exit(1);
}
int main(int argc, char **argv)
{
struct fdelay_board *b;
struct fdelay_pulse p;
int ch, err, dev = -1, raw = 0, opt;
/* Standard part of the file (repeated code) */
if (tools_need_help(argc, argv))
help(argv[0]);
/* print versions if needed */
print_version(argc, argv);
err = fdelay_init();
if (err) {
fprintf(stderr, "%s: library initialization failed\n", argv[0]);
exit(1);
}
while ((opt = getopt(argc, argv, "d:rh")) != -1) {
char *rest;
switch (opt) {
case 'd':
dev = strtol(optarg, &rest, 0);
if (rest && *rest) {
fprintf(stderr, "%s: Not a number \"%s\"\n",
argv[0], optarg);
exit(1);
}
break;
case 'r':
raw = 1;
break;
case 'h':
help(argv[0]);
exit(0);
}
}
if (dev < 0) {
fprintf(stderr, "%s: several boards, please pass -d\n",
argv[0]);
exit(1);
}
b = fdelay_open(dev);
if (!b) {
fprintf(stderr, "%s: fdelay_open(): %s\n", argv[0],
strerror(errno));
exit(1);
}
for (ch = 1; ch <= 4; ch++) {
if (fdelay_get_config_pulse(b, FDELAY_OUTPUT_USER_TO_HW(ch),
&p) < 0) {
fprintf(stderr, "%s: get_config(channel %i): %s\n",
argv[0], ch, strerror(errno));
}
/* pass hw number again, as the function is low-level */
report_output_config(FDELAY_OUTPUT_USER_TO_HW(ch),
&p, raw ? TOOLS_UMODE_RAW : TOOLS_UMODE_USER);
}
fdelay_close(b);
fdelay_exit();
return 0;
}
/* Simple demo that acts on the termination of the first board */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <errno.h>
#include "fdelay-lib.h"
#include "tools-common.h"
char git_version[] = "git version: " GIT_VERSION;
void help(char *name)
{
fprintf(stderr, "%s: Use \"%s [-V] [-d <dev>] [on|off]\n",
name, name);
exit(1);
}
int main(int argc, char **argv)
{
struct fdelay_board *b;
int hwval, newval;
int dev = -1;
int err;
/* Standard part of the file (repeated code) */
if (tools_need_help(argc, argv))
help(argv[0]);
/* print versions if needed */
print_version(argc, argv);
err = fdelay_init();
if (err) {
fprintf(stderr, "%s: library initialization failed\n", argv[0]);
exit(1);
}
tools_getopt_d_i(argc, argv, &dev);
if (dev < 0) {
fprintf(stderr, "%s: several boards, please pass -d\n",
argv[0]);
exit(1);
}
/* Parse the extra argument, if any */
newval = -1;
if (optind == argc - 1) {
char *s = argv[optind];
if (!strcmp(s, "0") || !strcmp(s, "off"))
newval = 0;
else if (!strcmp(s, "1") || !strcmp(s, "on"))
newval = 1;
else
help(argv[0]);
}
/* Finally work */
b = fdelay_open(dev);
if (!b) {
fprintf(stderr, "%s: fdelay_open(): %s\n", argv[0],
strerror(errno));
exit(1);
}
hwval = fdelay_get_config_tdc(b);
switch(newval) {
case 1:
hwval |= FD_TDCF_TERM_50;
err = fdelay_set_config_tdc(b, hwval);
break;
case 0:
hwval &= ~FD_TDCF_TERM_50;
err = fdelay_set_config_tdc(b, hwval);
break;
}
if (err)
{
fprintf(stderr, "%s: error setting termination: %s", argv[0], strerror(errno));
exit(1);
}
printf("%s: termination is %s\n", argv[0],
(hwval & FD_TDCF_TERM_50) ? "on" : "off");
fdelay_close(b);
fdelay_exit();
return 0;
}
/*
* Simple code that is repeated over several tools
*/
extern void tools_getopt_d_i(int argc, char **argv, int *dev);
extern int tools_need_help(int argc, char **argv);
#define TOOLS_UMODE_USER 0
#define TOOLS_UMODE_RAW 1
#define TOOLS_UMODE_FLOAT 2
extern void tools_report_time(char *name, struct fdelay_time *t, int umode);
extern void report_output_config(int channel, struct fdelay_pulse *p, int umode);
extern void help(char *name); /* This is mandatory in all tools */
extern void print_version(int argc, char **argv);
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <inttypes.h>
#include "fdelay-lib.h"
#include "tools-common.h"
extern void help(char *name); /* This is mandatory in all tools */
extern char git_version[];
void print_version(int argc, char **argv)
{
if ((argc == 2) && (!strcmp(argv[1], "-V"))) {
printf("%s %s\n", argv[0], git_version);
printf("%s\n", libfdelay_version_s);
printf("%s\n", libfdelay_zio_version_s);
exit(0);
}
}
void tools_getopt_d_i(int argc, char **argv, int *dev)
{
char *rest;
int opt;
while ((opt = getopt(argc, argv, "d:h")) != -1) {
switch (opt) {
case 'd':
*dev = strtol(optarg, &rest, 0);
if (rest && *rest) {
fprintf(stderr, "%s: Not a number \"%s\"\n",
argv[0], optarg);
exit(1);
}
break;
case 'h':
help(argv[0]);
}
}
}
int tools_need_help(int argc, char **argv)
{
if (argc != 2)
return 0;
if (!strcmp(argv[1], "--help"))
return 1;
return 0;
}
void tools_report_time(char *name, struct fdelay_time *t, int umode)
{
unsigned long long picoseconds =
t->coarse * 8000ULL +
t->frac * 8000ULL / 4096ULL;
printf("%s ", name);
switch(umode) {
case TOOLS_UMODE_USER:
printf ("%10"PRIu64":%03llu,%03llu,%03llu,%03llu ps\n",
t->utc,
(picoseconds / (1000ULL * 1000 * 1000)),
(picoseconds / (1000ULL * 1000) % 1000),
(picoseconds / (1000ULL) % 1000),
(picoseconds % 1000ULL));
break;
case TOOLS_UMODE_FLOAT:
printf ("float %10"PRIu64".%012llu\n", t->utc,
picoseconds);
break;
case TOOLS_UMODE_RAW:
printf("raw utc %10"PRIu64", coarse %9"PRIu32", frac %9"PRIu32"\n",
t->utc, t->coarse, t->frac);
break;
}
}
static struct fdelay_time fd_ts_sub(struct fdelay_time a, struct fdelay_time b)
{
struct fdelay_time rv;
int f, c = 0;
int64_t u = 0;
f = a.frac - b.frac;
if(f < 0)
{
f += 4096;
c--;
}
c += a.coarse - b.coarse;
if(c < 0)
{
c += 125 * 1000 * 1000;
u--;
}
u += a.utc - b.utc;
rv.utc = u;
rv.coarse = c;
rv.frac = f;
rv.seq_id = 0;
rv.channel = 0;
return rv;
}
static void report_output_config_human(int channel, struct fdelay_pulse *p)
{
struct fdelay_time width;
printf("Channel %i: ", FDELAY_OUTPUT_HW_TO_USER(channel));
int m = p->mode & 0x7f;
switch(m)
{
case FD_OUT_MODE_DISABLED:
printf("disabled\n");
return;
case FD_OUT_MODE_PULSE:
printf("pulse generator mode");
break;
case FD_OUT_MODE_DELAY:
printf("delay mode");
break;
default:
printf("unknown mode\n");
return;
}
if(p->mode & 0x80)
printf(" (triggered) ");
tools_report_time(m == FD_OUT_MODE_DELAY ? "\n delay: " : "\n start at: ",
&p->start, TOOLS_UMODE_USER);
width = fd_ts_sub(p->end, p->start);
tools_report_time(" pulse width: ", &width, TOOLS_UMODE_USER);
if(p->rep != 1)
{
printf(" repeat: ");
if(p->rep == -1)
printf("infinite\n");
else
printf("%d times\n", p->rep);
tools_report_time(" period: ", &p->loop, TOOLS_UMODE_USER);
}
}
void report_output_config_raw(int channel, struct fdelay_pulse *p, int umode)
{
char mode[80];
int m = p->mode & 0x7f;
if (m == FD_OUT_MODE_DISABLED) strcpy(mode, "disabled");
else if (m == FD_OUT_MODE_PULSE) strcpy(mode, "pulse");
else if (m == FD_OUT_MODE_DELAY) strcpy(mode, "delay");
else sprintf(mode, "%i (0x%04x)", p->mode, p->mode);
if (p->mode & 0x80)
strcat(mode, " (triggered)");
printf("Channel %i, mode %s, repeat %i %s\n",
FDELAY_OUTPUT_HW_TO_USER(channel), mode,
p->rep, p->rep == -1 ? "(infinite)" : "");
tools_report_time("start", &p->start, umode);
tools_report_time("end ", &p->end, umode);
tools_report_time("loop ", &p->loop, umode);
}
void report_output_config(int channel, struct fdelay_pulse *p, int umode)
{
switch(umode)
{
case TOOLS_UMODE_USER:
report_output_config_human(channel, p);
break;
case TOOLS_UMODE_RAW:
case TOOLS_UMODE_FLOAT:
report_output_config_raw(channel, p, umode);
default:
break;
}
}
zio @ 0765fa5d
Subproject commit 0765fa5d25466524889e9f5fb8976fbf429fabb3
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