diff --git a/kernel/wbgen-regs/Makefile b/kernel/wbgen-regs/Makefile index f03dc252ee19cb3eff0b33c607d0d7e2946616ec..fda99e88e725b2e0336f2fd763769b073a39f22e 100644 --- a/kernel/wbgen-regs/Makefile +++ b/kernel/wbgen-regs/Makefile @@ -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" diff --git a/kernel/wbgen-regs/gen10mhz-regs.h b/kernel/wbgen-regs/gen10mhz-regs.h new file mode 100644 index 0000000000000000000000000000000000000000..c5ac57697df8597c9e95244dc1f682be8c32daf3 --- /dev/null +++ b/kernel/wbgen-regs/gen10mhz-regs.h @@ -0,0 +1,100 @@ +/* + 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 diff --git a/kernel/wbgen-regs/gen10mhz-regs.wb b/kernel/wbgen-regs/gen10mhz-regs.wb new file mode 100644 index 0000000000000000000000000000000000000000..376507bbb069a1a7095320255e7b55cda4e2f9ae --- /dev/null +++ b/kernel/wbgen-regs/gen10mhz-regs.wb @@ -0,0 +1,128 @@ +-- -*- 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; + }; + }; + +} diff --git a/kernel/wbgen-regs/ppsg-regs.h b/kernel/wbgen-regs/ppsg-regs.h index 8976c557a64a6583d05b20c3dd4660a50465a3c1..512602f7872cadbd7397b2b890b137f33bd0b19f 100644 --- a/kernel/wbgen-regs/ppsg-regs.h +++ b/kernel/wbgen-regs/ppsg-regs.h @@ -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; diff --git a/kernel/wbgen-regs/ppsg-regs.wb b/kernel/wbgen-regs/ppsg-regs.wb index 7f1e2868e58984ca25bdbf24555f1aba7442387e..427b9ac9558c2348473f0c553969c48be39b7313 100644 --- a/kernel/wbgen-regs/ppsg-regs.wb +++ b/kernel/wbgen-regs/ppsg-regs.wb @@ -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"; + }; + }; }; diff --git a/userspace/include/fpga_io.h b/userspace/include/fpga_io.h index 8f33dd4af43be870fc32576c558d42a8d3b3ca4b..ff17dfa653187b774d329817c5b1cb977fe40aed 100644 --- a/userspace/include/fpga_io.h +++ b/userspace/include/fpga_io.h @@ -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 diff --git a/userspace/tools/.gitignore b/userspace/tools/.gitignore index 10da3e87a81182cd5f9d2a6cd4fb7a4e15c03dbd..8c81189e710a896c59fd077f1eac0a41352d7a16 100644 --- a/userspace/tools/.gitignore +++ b/userspace/tools/.gitignore @@ -18,3 +18,4 @@ wrs_vlans wrs_dump_shmem sdb-read nbtee +wrs_auxclk diff --git a/userspace/tools/Makefile b/userspace/tools/Makefile index b895e9d8996c04aa99f32d9dba265dbd1d09f719..14e0520746b2f15f5b5e7fbe36d9c487e40ad748 100644 --- a/userspace/tools/Makefile +++ b/userspace/tools/Makefile @@ -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 diff --git a/userspace/tools/wrs_auxclk.c b/userspace/tools/wrs_auxclk.c new file mode 100644 index 0000000000000000000000000000000000000000..2cbbc200b6ce83776899f33b32a15d00736e8658 --- /dev/null +++ b/userspace/tools/wrs_auxclk.c @@ -0,0 +1,147 @@ +#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; +}