Dear Mr. Cochran,

I've finally got back to my plan (ext.PPS to 2-4 i210 chips for 
tcpdump timestamping)  and I'm reviewing your proggie. 
Yes the code is pretty self-explanatory :-) and packed with gems.

If I understand correctly, setting up the SDP0 GPIO for external
PPS input (which takes two function calls in your program)
results in the respective phc throwing "events" on every PPS 
leading edge - passing them to the user space proggie that 
keeps waiting in a poll(), and keeps processing the events
thus received. The event essentially contains a precise timestamp (as 
per the i210's timebase) of the PPS leading edge.
And, the "servo" is not autonomous (in hardware or in kernel), it is 
in fact implemented by the synbc program, which has to explicitly 
instruct the PHC to adjust stepwise or frequency-wise accordingly...
the required "ppb" value actually comes from the servo->sample() 
PTP4l library function...

Interestingly to me, your program seems to instruct the "slave PHC"
(PPS master) to send the pulses every 2 seconds, rather than every 1 
second... am I reading this correctly?

int index = 0, perout = 2000000000;

I understand that the PHC's work in wall time,
and I should be able to prime them with the system time, 
once on program startup (as I won't have a PPS master / PTP slave PHC 
in my system). It's just a matter of asking clock_gettime() of the 
system timebase, rather than a particular PHC, in your function 
synbc_settime().

Interesting stuff. Thank you :-)

Frank Rysanek


On 8 Dec 2017 at 15:59, Richard Cochran wrote:
>
> On Fri, Dec 08, 2017 at 11:09:40PM +0000, Keller, Jacob E wrote:
> > I'm thinking the best way is to use the external timestamp events setup, 
> > and then plug that in as the pps source into phc2sys?
> > 
> > Does this make any sense, or am I paddling up the wrong creek?
> 
> So you *could* extend phc2sys, but that program is complex enough as
> is.  I have made thoughts about a successor to phc2sys that would
> handle gpio based measurements, including setting the pin functions
> using the PHC ioctls.
> 
> But for now, I would just write a simple program for your specific
> setup.  Below is an example for using three i210 cards whose first SDP
> are connected.  The first card is hard coded as the PPS producer.  In
> a more realistic JBOD setting, you would want to switch the PPS
> producer to be the PHC of the port that takes on the SLAVE role.
> 
> HTH,
> Richard
> 
> --->8---
> /**
>  * @file synbc.c
>  * @brief Synchronize the ports of a JBOD Boundary Clock.
>  * @note Copyright (C) 2015 Richard Cochran <richardcoch...@gmail.com>
>  *
>  * 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.
>  */
> #include <errno.h>
> #include <poll.h>
> #include <stdio.h>
> #include <string.h>
> #include <sys/ioctl.h>
> 
> #include <linux/ptp_clock.h>
> 
> #include "clockadj.h"
> #include "config.h"
> #include "phc.h"
> #include "print.h"
> #include "servo.h"
> #include "util.h"
> 
> #define N_PHC 3
> 
> static int synbc_extts(int fd, int enable)
> {
>       struct ptp_extts_request extts_request;
>       int index = 0;
>       memset(&extts_request, 0, sizeof(extts_request));
>       extts_request.index = index;
>       extts_request.flags = enable ? PTP_ENABLE_FEATURE : 0;
>       if (ioctl(fd, PTP_EXTTS_REQUEST, &extts_request)) {
>               pr_err("PTP_EXTTS_REQUEST");
>               return -1;
>       }
>       pr_info("external time stamp request okay");
>       return 0;
> }
> 
> static int synbc_pin_input(int fd)
> {
>       struct ptp_pin_desc desc;
>       int index = 0, pin_index = 0;
> 
>       memset(&desc, 0, sizeof(desc));
>       desc.index = pin_index;
>       desc.func = PTP_PF_EXTTS;
>       desc.chan = index;
>       if (ioctl(fd, PTP_PIN_SETFUNC, &desc)) {
>               pr_err("PTP_PIN_SETFUNC");
>               return -1;
>       }
>       pr_info("set pin function okay");
>       return 0;
> }
> 
> static int synbc_pin_output(int fd)
> {
>       struct ptp_pin_desc desc;
>       int index = 0, pin_index = 0;
> 
>       memset(&desc, 0, sizeof(desc));
>       desc.index = pin_index;
>       desc.func = PTP_PF_PEROUT;
>       desc.chan = index;
>       if (ioctl(fd, PTP_PIN_SETFUNC, &desc)) {
>               pr_err("PTP_PIN_SETFUNC");
>               return -1;
>       }
>       pr_info("set pin function okay");
>       return 0;
> }
> 
> static int synbc_pps_output(clockid_t clkid, int fd)
> {
>       struct ptp_perout_request perout_request;
>       struct timespec ts;
>       int index = 0, perout = 2000000000;
> 
>       if (clock_gettime(clkid, &ts)) {
>               pr_err("clock_gettime");
>               return -1;
>       }
>       memset(&perout_request, 0, sizeof(perout_request));
>       perout_request.index = index;
>       /*disable old setting*/
>       if (ioctl(fd, PTP_PEROUT_REQUEST, &perout_request)) {
>               pr_err("PTP_PEROUT_REQUEST");
>               return -1;
>       }
>       perout_request.start.sec = ts.tv_sec + 2;
>       perout_request.start.nsec = 0;
>       perout_request.period.sec = 0;
>       perout_request.period.nsec = perout;
>       if (ioctl(fd, PTP_PEROUT_REQUEST, &perout_request)) {
>               pr_err("PTP_PEROUT_REQUEST");
>               return -1;
>       }
>       pr_info("periodic output request okay");
>       return 0;
> }
> 
> static int synbc_settime(clockid_t *clkid)
> {
>       struct timespec ts;
>       int i;
>       if (clock_gettime(clkid[0], &ts)) {
>               pr_err("clock_gettime");
>               return -1;
>       }
>       for (i = 1; i < N_PHC; i++) {
>               if (clock_settime(clkid[i], &ts)) {
>                       pr_err("clock_settime");
>                       return -1;
>               }
>       }
>       return 0;
> }
> 
> int main()
> {
>       int fd[N_PHC], i;
>       clockid_t clkid[N_PHC];
>       struct config *config;
>       struct pollfd pfd[N_PHC - 1];
>       struct servo *servo[N_PHC - 1];
>       char device[64];
> 
>       handle_term_signals();
>       print_set_progname("synbc");
>       print_set_verbose(1);
>       print_set_syslog(0);
> 
>       config = config_create();
>       if (!config)
>               return -1;
> 
>       for (i = 0; i < N_PHC; i++) {
>               snprintf(device, sizeof(device), "/dev/ptp%d", i);
>               clkid[i] = phc_open(device);
>               if (CLOCK_INVALID == clkid[i]) {
>                       pr_err("Failed to open %s: %m", device);
>                       return -1;
>               }
>               fd[i] = CLOCKID_TO_FD(clkid[i]);
>       }
> 
>       for (i = 1; i < N_PHC; i++) {
>               servo[i - 1] = servo_create(config, CLOCK_SERVO_PI, 0, 100000, 
> 0);
>               if (!servo[i - 1])
>                       return -1;
>               servo_sync_interval(servo[i - 1], 1.0);
>       }
> 
>       if (synbc_settime(clkid))
>               return -1;
> 
>       /* Configure a 1 PPS between the cards. */
> 
>       if (synbc_pin_output(fd[0]))
>               return -1;
> 
>       for (i = 1; i < N_PHC; i++)
>               if (synbc_pin_input(fd[i]))
>                       return -1;
> 
>       if (synbc_pps_output(clkid[0], fd[0]))
>               return -1;
> 
>       for (i = 1; i < N_PHC; i++)
>               if (synbc_extts(fd[i], 1))
>                       return -1;
> 
>       for (i = 1; i < N_PHC; i++) {
>               pfd[i - 1].fd = fd[i];
>               pfd[i - 1].events = POLLIN | POLLPRI;
>       }
> 
>       while (is_running()) {
>               int cnt = poll(pfd, N_PHC - 1, 1000);
>               if (cnt < 0) {
>                       if (EINTR == errno) {
>                               continue;
>                       } else {
>                               pr_emerg("poll failed");
>                               return -1;
>                       }
>               } else if (!cnt) {
>                       pr_err("no PPS");
>                       continue;
>               }
>               for (i = 1; i < N_PHC; i++) {
>                       if (pfd[i - 1].revents & (POLLIN | POLLPRI)) {
> 
>                               struct ptp_extts_event event;
>                               enum servo_state state;
>                               double ppb;
>                               int64_t offset;
>                               uint64_t ts;
> 
>                               cnt = read(fd[i], &event, sizeof(event));
>                               if (cnt != sizeof(event)) {
>                                       perror("read");
>                                       break;
>                               }
>                               pr_debug("ptp%d event at %lld.%09u", i,
>                                        event.t.sec, event.t.nsec);
> 
>                               ts = event.t.sec * NS_PER_SEC;
>                               ts += event.t.nsec;
>                               offset = ts % NS_PER_SEC;
>                               if (offset > NS_PER_SEC / 2)
>                                       offset -= NS_PER_SEC;
> 
>                               ppb = servo_sample(servo[i - 1], offset, ts,
>                                                  1.0, &state);
> 
>                               pr_info("ptp%d offset %10" PRId64 " s%d freq 
> %+7.0f ",
>                                       i, offset, state, ppb);
> 
>                               switch (state) {
>                               case SERVO_UNLOCKED:
>                                       break;
>                               case SERVO_JUMP:
>                                       clockadj_step(clkid[i], -offset);
>                                       /* Fall through. */
>                               case SERVO_LOCKED:
>                                       clockadj_set_freq(clkid[i], -ppb);
>                                       break;
>                               }
> 
>                       }
>               }
>       }
> 
>       for (i = 1; i < N_PHC; i++)
>               if (synbc_extts(fd[i], 0))
>                       pr_err("hm");
> 
>       for (i = 0; i < N_PHC; i++)
>               phc_close(clkid[i]);
> 
>       for (i = 1; i < N_PHC; i++)
>               servo_destroy(servo[i - 1]);
> 
>       config_destroy(config);
> 
>       return 0;
> }
> 
> 
> ------------------------------------------------------------------------------
> Check out the vibrant tech community on one of the world's most
> engaging tech sites, Slashdot.org! http://sdm.link/slashdot
> _______________________________________________
> Linuxptp-devel mailing list
> Linuxptp-devel@lists.sourceforge.net
> https://lists.sourceforge.net/lists/listinfo/linuxptp-devel



------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
_______________________________________________
Linuxptp-devel mailing list
Linuxptp-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linuxptp-devel

Reply via email to