Commit b41e8354 authored by Grzegorz Daniluk's avatar Grzegorz Daniluk Committed by Alessandro Rubini

userspace/tools: adding simple tools for setting CLK2 output

parent 58775a73
......@@ -16,10 +16,11 @@ WB_PPSG = $(MODULES_WRC)/wr_pps_gen/pps_gen_wb.wb
WB_TSTAMP = $(MODULES_WRS)/wrsw_txtsu/wrsw_txtsu.wb
WB_RTU = $(MODULES_WRS)/wrsw_rtu/rtu_wishbone_slave.wb
WB_NIC = $(MODULES_WRS)/wrsw_nic/wr_nic.wb
WB_GEN10 = $(MODULES_WRS)/wrsw_rt_subsystem/wrsw_gen_10mhz.wb
WB_SOFTPLL = $(MODULES_WRC)/wr_softpll_ng/spll_wb_slave.wb
HEADERS = endpoint-regs.h endpoint-mdio.h ppsg-regs.h tstamp-regs.h rtu-regs.h \
nic-regs.h softpll-regs.h pstats-regs.h
nic-regs.h softpll-regs.h pstats-regs.h gen10mhz-regs.h
WBINPUT = $(HEADERS:.h=wb)
# No default, for people who types "make" everywhere (like me)
......@@ -47,4 +48,5 @@ wbinput:
@cp $(WB_RTU) rtu-regs.wb
@cp $(WB_NIC) nic-regs.wb
@cp $(WB_SOFTPLL) softpll-regs.wb
@cp $(WB_GEN10) gen10mhz-regs.wb
@echo "Copied input files from subversions to local directory"
/*
Register definitions for slave core: WR Switch aux clock generation module
* File : gen10mhz-regs.h
* Author : auto-generated by wbgen2 from gen10mhz-regs.wb
* Standard : ANSI C
THIS FILE WAS GENERATED BY wbgen2 FROM SOURCE FILE gen10mhz-regs.wb
DO NOT HAND-EDIT UNLESS IT'S ABSOLUTELY NECESSARY!
*/
#ifndef __WBGEN2_REGDEFS_GEN10MHZ
#define __WBGEN2_REGDEFS_GEN10MHZ
#ifdef __KERNEL__
#include <linux/types.h>
#else
#include <stdint.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: Period Register */
/* definitions for field: Half period width in reg: Period Register */
#define GEN10_PR_HP_WIDTH_MASK WBGEN2_GEN_MASK(0, 16)
#define GEN10_PR_HP_WIDTH_SHIFT 0
#define GEN10_PR_HP_WIDTH_W(value) WBGEN2_GEN_WRITE(value, 0, 16)
#define GEN10_PR_HP_WIDTH_R(reg) WBGEN2_GEN_READ(reg, 0, 16)
/* definitions for register: Duty Cycle Register */
/* definitions for field: Low state width in reg: Duty Cycle Register */
#define GEN10_DCR_LOW_WIDTH_MASK WBGEN2_GEN_MASK(0, 16)
#define GEN10_DCR_LOW_WIDTH_SHIFT 0
#define GEN10_DCR_LOW_WIDTH_W(value) WBGEN2_GEN_WRITE(value, 0, 16)
#define GEN10_DCR_LOW_WIDTH_R(reg) WBGEN2_GEN_READ(reg, 0, 16)
/* definitions for register: Coarse Shift Register */
/* definitions for register: IODelay Register */
/* definitions for field: Required delay value in reg: IODelay Register */
#define GEN10_IOR_TAP_SET_MASK WBGEN2_GEN_MASK(0, 5)
#define GEN10_IOR_TAP_SET_SHIFT 0
#define GEN10_IOR_TAP_SET_W(value) WBGEN2_GEN_WRITE(value, 0, 5)
#define GEN10_IOR_TAP_SET_R(reg) WBGEN2_GEN_READ(reg, 0, 5)
/* definitions for field: Current delay value read from IODelay in reg: IODelay Register */
#define GEN10_IOR_TAP_CUR_MASK WBGEN2_GEN_MASK(8, 5)
#define GEN10_IOR_TAP_CUR_SHIFT 8
#define GEN10_IOR_TAP_CUR_W(value) WBGEN2_GEN_WRITE(value, 8, 5)
#define GEN10_IOR_TAP_CUR_R(reg) WBGEN2_GEN_READ(reg, 8, 5)
/* definitions for field: IOdelay locked in reg: IODelay Register */
#define GEN10_IOR_LCK WBGEN2_GEN_MASK(31, 1)
/* definitions for register: PPS IODelay Register */
/* definitions for field: Required delay value in reg: PPS IODelay Register */
#define GEN10_PPS_IOR_TAP_SET_MASK WBGEN2_GEN_MASK(0, 5)
#define GEN10_PPS_IOR_TAP_SET_SHIFT 0
#define GEN10_PPS_IOR_TAP_SET_W(value) WBGEN2_GEN_WRITE(value, 0, 5)
#define GEN10_PPS_IOR_TAP_SET_R(reg) WBGEN2_GEN_READ(reg, 0, 5)
/* definitions for field: Current delay value read from IODelay in reg: PPS IODelay Register */
#define GEN10_PPS_IOR_TAP_CUR_MASK WBGEN2_GEN_MASK(8, 5)
#define GEN10_PPS_IOR_TAP_CUR_SHIFT 8
#define GEN10_PPS_IOR_TAP_CUR_W(value) WBGEN2_GEN_WRITE(value, 8, 5)
#define GEN10_PPS_IOR_TAP_CUR_R(reg) WBGEN2_GEN_READ(reg, 8, 5)
PACKED struct GEN10_WB {
/* [0x0]: REG Period Register */
uint32_t PR;
/* [0x4]: REG Duty Cycle Register */
uint32_t DCR;
/* [0x8]: REG Coarse Shift Register */
uint32_t CSR;
/* [0xc]: REG IODelay Register */
uint32_t IOR;
/* [0x10]: REG PPS IODelay Register */
uint32_t PPS_IOR;
};
#endif
-- -*- Mode: LUA; tab-width: 2 -*-
-- White-Rabbit 10 MHz Clock Generation
-- author: Grzegorz Daniluk <grzegorz.daniluk@cern.ch>
--
-- Use wbgen2 to generate code, documentation and more.
-- wbgen2 is available at:
-- http://www.ohwr.org/projects/wishbone-gen
--
peripheral {
name = "WR Switch aux clock generation module";
decription = "The module allows gerating WR-aligned clock of a given \
frequency, duty cycle and phase. By default it is configured to generate 10MHz \
signal.";
hdl_entity = "gen10_wishbone_slave";
prefix = "gen10";
reg {
name = "Period Register";
prefix = "PR";
field {
name = "Half period width";
description = "Defined as a number of 2ns cycles.";
prefix = "HP_WIDTH";
size = 16;
type = PASS_THROUGH;
access_dev = READ_ONLY;
access_bus = READ_WRITE;
};
};
reg {
name = "Duty Cycle Register";
prefix = "DCR";
field {
name = "Low state width";
description = "Defined as a number of 2ns cycles. \
Used together with PR register can be used to generate a square wave with a duty \
cycle different than 0,5.";
prefix = "LOW_WIDTH";
size = 16;
type = PASS_THROUGH;
access_dev = READ_ONLY;
access_bus = READ_WRITE;
};
};
reg {
name = "Coarse Shift Register";
prefix = "CSR";
field {
name = "Coarse shift value in 2ns cycles.";
description = "MUST be not larger than the required clock period";
size = 16;
type = PASS_THROUGH;
access_dev = READ_ONLY;
access_bus = READ_WRITE;
};
};
reg {
name = "IODelay Register";
description = "IODelay may be used if generated signal is in phase with 500MHz \
clock from AD9516 fed to the flip-flop. In that situation clock signal on CLK2 \
output will be jittering by 2ns. Phase shifting it with IODelay eliminates \
this problem.";
prefix = "IOR";
field {
name = "Required delay value";
prefix = "TAP_SET";
size = 5;
type = PASS_THROUGH;
access_dev = READ_ONLY;
access_bus = WRITE_ONLY;
};
field {
name = "Current delay value read from IODelay";
prefix = "TAP_CUR";
align = 8;
size = 5;
type = SLV;
access_dev = WRITE_ONLY;
access_bus = READ_ONLY;
};
field {
name = "IOdelay locked";
prefix = "LCK";
align = 31;
size = 1;
type = BIT;
access_dev = WRITE_ONLY;
access_bus = READ_ONLY;
};
};
reg {
name = "PPS IODelay Register";
description = "Used to control IODelay attached to 1-PPS signal generated \
from the switch. It can be used to preciesly align 1-PPS with CLK2 out.";
prefix = "PPS_IOR";
field {
name = "Required delay value";
prefix = "TAP_SET";
size = 5;
type = PASS_THROUGH;
access_dev = READ_ONLY;
access_bus = WRITE_ONLY;
};
field {
name = "Current delay value read from IODelay";
prefix = "TAP_CUR";
align = 8;
size = 5;
type = SLV;
access_dev = WRITE_ONLY;
access_bus = READ_ONLY;
};
};
}
......@@ -78,6 +78,12 @@
/* definitions for field: Timecode output(UTC+cycles) valid in reg: External sync control register */
#define PPSG_ESCR_TM_VALID WBGEN2_GEN_MASK(2, 1)
/* definitions for field: Set seconds counter in reg: External sync control register */
#define PPSG_ESCR_SEC_SET WBGEN2_GEN_MASK(3, 1)
/* definitions for field: Set nanoseconds counter in reg: External sync control register */
#define PPSG_ESCR_NSEC_SET WBGEN2_GEN_MASK(4, 1)
PACKED struct PPSG_WB {
/* [0x0]: REG Control Register */
uint32_t CR;
......
......@@ -192,6 +192,24 @@ peripheral {
clock = "refclk_i";
};
field {
name = "Set seconds counter";
description = "write 1: set seconds counter to the value stored in ADJ_UTCLO and ADJ_UTCHI. Nanoseconds counter stays unchanged.\
write 0: no effect";
prefix = "SEC_SET";
type = MONOSTABLE;
clock = "refclk_i";
};
field {
name = "Set nanoseconds counter";
description = "write 1: set nanoseconds counter to the value stored in ADJ_NSEC. Seconds counter stays unchanged.\
write 0: no effect";
prefix = "NSEC_SET";
type = MONOSTABLE;
clock = "refclk_i";
};
};
};
......@@ -8,6 +8,9 @@
/* PPS Generator */
#define FPGA_BASE_PPS_GEN 0x10500
/* Aux Clk Generator */
#define FPGA_BASE_GEN_10MHZ 0x10600
/* Routing Table */
#define FPGA_BASE_RTU 0x60000
......
......@@ -18,3 +18,4 @@ wrs_vlans
wrs_dump_shmem
sdb-read
nbtee
wrs_auxclk
......@@ -4,6 +4,7 @@ TOOLS += wrs_version wr_date lm32-vuart wrs_pstats
TOOLS += wrs_vlans wrs_dump_shmem
TOOLS += sdb-read
TOOLS += nbtee
TOOLS += wrs_auxclk
WR_INSTALL_ROOT ?= /usr/lib/white-rabbit
......
#include <stdio.h>
#include <getopt.h>
#include <unistd.h>
#include <stdlib.h>
#include <stddef.h>
#include <libwr/switch_hw.h>
#include "fpga_io.h"
#include "regs/gen10mhz-regs.h"
#include "regs/ppsg-regs.h"
#define OPT_HELP 'h'
#define OPT_FREQ 2
#define OPT_DUTY 3
#define OPT_CSHIFT 4
#define OPT_SIGDEL 5
#define OPT_PPSHIFT 6
/* default parameters to generate 10MHz signal */
#define DEF_FREQ 10
#define DEF_DUTY 0.5
#define DEF_CSHIFT 30
#define DEF_SIGDEL 0
#define DEF_PPSHIFT 0
#define MAX_FREQ 250 /* min half-period is 2ns */
#define MIN_FREQ 0.004 /* max half-period is 65535*2ns */
#define CNT_RES 2
#define gen10_write(reg, val) \
_fpga_writel(FPGA_BASE_GEN_10MHZ + offsetof(struct GEN10_WB, reg), val)
#define gen10_read(reg) \
_fpga_readl(FPGA_BASE_GEN_10MHZ + offsetof(struct GEN10_WB, reg))
/* runtime options */
struct option ropts[] = {
{"help", 0, NULL, OPT_HELP},
{"freq", 1, NULL, OPT_FREQ},
{"duty", 1, NULL, OPT_DUTY},
{"cshift", 1, NULL, OPT_CSHIFT},
{"sigdel", 1, NULL, OPT_SIGDEL},
{"ppshift", 1, NULL, OPT_PPSHIFT},
{0,}};
/*******************/
int print_help(char *prgname)
{
fprintf(stderr, "Use: %s [--freq <MHz>] [--duty <frac>] [--cshift <ns>]"
" [--ppshift <taps>] [--sigdel <taps>]\n", prgname);
return 0;
}
int apply_settings(float freq_mhz, float duty, int cshift_ns, int sigdel_taps,
int ppshift_taps)
{
int period_ns;
int h_width, l_width;
/*first check if values are in range*/
if( freq_mhz > MAX_FREQ || freq_mhz < MIN_FREQ ) {
fprintf(stderr, "Frequency outside range <%f; %d>\n", MIN_FREQ,
MAX_FREQ);
return -1;
}
if( !(duty > 0 && duty < 1) ) {
fprintf(stderr, "Duty %f outside range (0; 1)\n", duty);
return -1;
}
/* calculate high and low width from frequency and duty */
period_ns = 1000 / freq_mhz;
h_width = period_ns/CNT_RES * duty;
l_width = period_ns/CNT_RES - h_width;
/* now check the coarse shift */
if( cshift_ns > period_ns || cshift_ns < 0 ) {
fprintf(stderr, "Coarse shift outside range <0; %d>\n",
period_ns);
return -1;
}
gen10_write(PR, h_width);
gen10_write(DCR, l_width);
gen10_write(CSR, cshift_ns/CNT_RES);
gen10_write(IOR, sigdel_taps);
gen10_write(PPS_IOR, ppshift_taps);
sleep(1);
/* now read the actual delay (in taps) from IODelays */
sigdel_taps = gen10_read(IOR);
sigdel_taps >>= GEN10_IOR_TAP_CUR_SHIFT;
ppshift_taps = gen10_read(PPS_IOR);
ppshift_taps >>= GEN10_PPS_IOR_TAP_CUR_SHIFT;
printf("Calculated settings:\n");
printf("period: %d ns (%d MHz)\n", period_ns, 1000/period_ns);
printf("high: %d ns; low: %d ns\n", h_width, l_width);
printf("duty: %f\n", (float)h_width*CNT_RES/period_ns);
printf("coarse shift: %d\n", (cshift_ns/CNT_RES)*CNT_RES);
printf("PPS shift: %d taps\n", ppshift_taps);
printf("Signal delay: %d taps\n", sigdel_taps);
return 0;
}
int main(int argc, char *argv[])
{
char *prgname = argv[0];
float freq_mhz = DEF_FREQ;
float duty = DEF_DUTY;
int cshift_ns = DEF_CSHIFT;
int sigdel_taps = DEF_SIGDEL;
int ppshift_taps = DEF_PPSHIFT;
int c;
if (shw_fpga_mmap_init() < 0) {
fprintf(stderr, "%s: Can't access device memory\n", prgname);
exit(1);
}
while( (c = getopt_long(argc, argv, "h", ropts, NULL)) != -1) {
switch(c) {
case OPT_FREQ:
freq_mhz = (float) atof(optarg);
break;
case OPT_DUTY:
duty = (float) atof(optarg);
break;
case OPT_CSHIFT:
cshift_ns = atoi(optarg);
break;
case OPT_SIGDEL:
sigdel_taps = atoi(optarg);
break;
case OPT_PPSHIFT:
ppshift_taps = atoi(optarg);
break;
case OPT_HELP:
default:
print_help(prgname);
return 0;
}
}
apply_settings(freq_mhz, duty, cshift_ns, sigdel_taps, ppshift_taps);
return 0;
}
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