Dear everyone (maybe Mr. Cochran especially), my setup with external PPS has started showing basic signs of life.
Interestingly, my two i210 cards seem to be throwing an event on both PPS edges, rising and falling :-) They're spaced 200 ms apart. And, somehow the system converges to the "wrong" edge, which probably is not a problem, and which I could readily correct by flipping polarity at the Meinberg clock, but for other reasons (a 25MHz synth that I'm working on) I would prefer to reconfigure the i210 accordingly. I seem to recall notes from the i210 chip datasheet that the polarity can be adjusted. If you can give me a shortcut on how to do this in the code of your example proggie, it would save me some more time... Maybe I should just check the tools from kernel.org that you've mentioned in your other message. Or back to devmem2. I'm attaching a crude beta of my servo proggie, based on Mr. Cochran's example. See its output below. BTW: Yippeee!! :-D Frank Rysanek i210_ext_pps[1477.035]: ptp4 offset -3 s2 freq -22542 i210_ext_pps[1477.835]: ptp1 offset -200015399 s2 freq -100000 i210_ext_pps[1477.835]: ptp4 offset -200012384 s2 freq -100000 i210_ext_pps[1478.035]: ptp1 offset -3 s2 freq -3720 i210_ext_pps[1478.035]: ptp4 offset 5 s2 freq -22534 i210_ext_pps[1478.835]: ptp1 offset -200015391 s2 freq -100000 i210_ext_pps[1478.835]: ptp4 offset -200012381 s2 freq -100000 i210_ext_pps[1479.035]: ptp1 offset -4 s2 freq -3722 i210_ext_pps[1479.035]: ptp4 offset -1 s2 freq -22539 i210_ext_pps[1479.835]: ptp1 offset -200015390 s2 freq -100000 i210_ext_pps[1479.835]: ptp4 offset -200012384 s2 freq -100000 i210_ext_pps[1480.035]: ptp1 offset -2 s2 freq -3721 i210_ext_pps[1480.035]: ptp4 offset -3 s2 freq -22541 i210_ext_pps[1480.835]: ptp1 offset -200015389 s2 freq -100000 i210_ext_pps[1480.835]: ptp4 offset -200012384 s2 freq -100000 i210_ext_pps[1481.035]: ptp1 offset 6 s2 freq -3714 i210_ext_pps[1481.035]: ptp4 offset 5 s2 freq -22534 i210_ext_pps[1481.835]: ptp1 offset -200015394 s2 freq -100000 i210_ext_pps[1481.835]: ptp4 offset -200012382 s2 freq -100000 i210_ext_pps[1482.035]: ptp1 offset 1 s2 freq -3717 i210_ext_pps[1482.035]: ptp4 offset -1 s2 freq -22539 i210_ext_pps[1482.835]: ptp1 offset -200015397 s2 freq -100000 i210_ext_pps[1482.835]: ptp4 offset -200012384 s2 freq -100000 i210_ext_pps[1483.035]: ptp1 offset -2 s2 freq -3720 i210_ext_pps[1483.035]: ptp4 offset -3 s2 freq -22541 i210_ext_pps[1483.835]: ptp1 offset -200015398 s2 freq -100000 i210_ext_pps[1483.835]: ptp4 offset -200012385 s2 freq -100000 i210_ext_pps[1484.035]: ptp1 offset -2 s2 freq -3720 i210_ext_pps[1484.035]: ptp4 offset -4 s2 freq -22543 i210_ext_pps[1484.835]: ptp1 offset -200015390 s2 freq -100000 i210_ext_pps[1484.835]: ptp4 offset -200012384 s2 freq -100000 i210_ext_pps[1485.035]: ptp1 offset -3 s2 freq -3722 i210_ext_pps[1485.035]: ptp4 offset 5 s2 freq -22535 i210_ext_pps[1485.835]: ptp1 offset -200015397 s2 freq -100000 i210_ext_pps[1485.835]: ptp4 offset -200012381 s2 freq -100000 i210_ext_pps[1486.035]: ptp1 offset -2 s2 freq -3722 i210_ext_pps[1486.035]: ptp4 offset 0 s2 freq -22539 i210_ext_pps[1486.835]: ptp1 offset -200015396 s2 freq -100000 i210_ext_pps[1486.835]: ptp4 offset -200012384 s2 freq -100000 i210_ext_pps[1487.035]: ptp1 offset -1 s2 freq -3721 i210_ext_pps[1487.035]: ptp4 offset -3 s2 freq -22542 On 13 Feb 2018 at 11:45, linuxptp-devel@lists.sourcefo wrote: > > 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 > >
The following section of this message contains a file attachment prepared for transmission using the Internet MIME message format. If you are using Pegasus Mail, or any other MIME-compliant system, you should be able to save it or view it from within your mailer. If you cannot, please ask your system administrator for assistance. ---- File information ----------- File: i210_ext_pps.c Date: 13 Feb 2018, 17:08 Size: 8281 bytes. Type: Program-source
i210_ext_pps.c
Description: Binary data
------------------------------------------------------------------------------ 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