Commit 125264a1 authored by Tomasz Wlostowski's avatar Tomasz Wlostowski Committed by Alessandro Rubini

wrsw_hal: added comments, merged some external source stuff from the old SVN repo

parent ed889612
/* Wrapper code for handling Lua configuration files */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
......@@ -11,19 +13,22 @@
static lua_State *cfg_file = NULL;
static char *extra_cmdline = NULL;
static char *hal_config_file = HAL_CONFIG_FILE;
/* Sets the path to the HAL config file */
void hal_config_set_config_file(const char *str)
{
hal_config_file = strdup(str);
}
/* Appends extra Lua code to the contents of the configuration file to be parsed. Can be used
to execute inline config given as a command line parameter. */
int hal_config_extra_cmdline(const char *str)
{
extra_cmdline = strdup(str);
}
/* Parses the HAL configuration file */
int hal_parse_config()
{
TRACE(TRACE_INFO, "Parsing wrsw_hal configuration file: %s", HAL_CONFIG_FILE);
......@@ -32,9 +37,13 @@ int hal_parse_config()
luaL_openlibs(cfg_file);
/* Just execute the config file as a regular Lua script. The contents of the file will be ordinary
Lua variables accessible via lua_State. */
if (luaL_dofile(cfg_file, hal_config_file))
TRACE(TRACE_ERROR, "Error parsing the configuration file: %s", lua_tostring(cfg_file,-1));
/* Declare a Lua "helper" function for regexp searching global variables - it's much easier to implement
in Lua than in plain C. */
luaL_dostring(cfg_file, "\
function get_var(name) \
local t = _G \
......@@ -44,12 +53,15 @@ int hal_parse_config()
return t \
end");
/* Execute extra code from the command line */
if(extra_cmdline)
luaL_dostring(cfg_file, extra_cmdline);
return 0;
}
/* Looks up for a global variable (name). If it's found, it is pushed on the Lua stack and
the function returns 0, otherwise a negative error code is returned. */
static int global_get_var(const char *name)
{
lua_getglobal(cfg_file, "get_var");
......@@ -60,6 +72,8 @@ static int global_get_var(const char *name)
return 0;
}
/* Retreives an integer variable (name) and stores it at (value). Returns 0 on success, -1 if the
variable was not found or has invalid format. */
int hal_config_get_int(const char *name, int *value)
{
if(global_get_var(name) < 0)
......@@ -70,6 +84,7 @@ int hal_config_get_int(const char *name, int *value)
return 0;
}
/* Same as above, but for double floating point numbers. */
int hal_config_get_double(const char *name, double *value)
{
if(global_get_var(name) < 0)
......@@ -80,6 +95,7 @@ int hal_config_get_double(const char *name, double *value)
return 0;
}
/* Same as above, but for null-terminated strings */
int hal_config_get_string(const char *name, char *value, int max_len)
{
if(global_get_var(name) < 0)
......@@ -90,6 +106,28 @@ int hal_config_get_string(const char *name, char *value, int max_len)
return 0;
}
/* Iterates a particular section (section) in the config file for its subsections. For a file containing:
ports = {
wru0 = {
...
};
wr1 = {
...
};
wr2 = {
...
};
};
calling hal_config_iterate("ports", 0, sub, strlen(sub)) will return 1 and sub == "wr0",
calling hal_config_iterate("ports", 1, sub, strlen(sub)) will return 1 and sub == "wr1",
hal_config_iterate("ports", 3, sub, strlen(sub)) will return 0, as there will be no more subsections in
the "ports" section. */
int hal_config_iterate(const char *section, int index, char *subsection, int max_len)
{
int i = 0;
......
/* HAL public API available via WR-IPC */
#include <stdio.h>
#include <stdlib.h>
#include <hw/trace.h>
#include <hw/dmpll.h>
#include <hw/dmpll.h> /* for direct access to DMPLL */
#include "wrsw_hal.h"
#include "hal_exports.h"
#include "hal_exports.h" /* for exported structs/function protos */
#include <wr_ipc.h>
#define WRSW_HAL_SERVER_ADDR "wrsw_hal"
static wripc_handle_t hal_ipc;
/* Dummy WRIPC export, used by the clients to check if the HAL is responding */
int halexp_check_running()
{
return 1;
}
/* External synchronization source (i.e. GPS 10 MHz input) control. */
int halexp_extsrc_cmd(int command)
{
int rval;
switch(command)
{
/* There's only one command so far: checking if a valid reference clock is present on the external input
and whether the PLL is locked to the network recovered clock or to the external reference. */
case HEXP_EXTSRC_CMD_CHECK:
rval = hal_extsrc_check_lock();
if(rval > 0)
return HEXP_EXTSRC_STATUS_LOCKED;
else if (!rval)
return HEXP_LOCK_STATUS_BUSY;
else
return HEXP_EXTSRC_STATUS_NOSRC;
break;
}
return -100; /* fixme: add real error code */
}
/* Dummy reset call (used to be a real reset for testing, but it's no longer necessary) */
int halexp_reset_port(const char *port_name)
{
TRACE(TRACE_INFO, "resetting port %s\n", port_name);
return 0;
}
/* Locking call - controls the HAL locking state machine. Called by the PTPd during the WR Link Setup phase, when
it has detected a compatible WR master. */
int halexp_lock_cmd(const char *port_name, int command, int priority)
{
int rval;
// TRACE(TRACE_INFO,"Command %d", command);
switch(command)
{
/* Start locking - i.e. tell the HAL locking state machine to use the port (port_name) as the source of the reference
frequency. (priority) parameter allows to distinguish between various reference sources and establish
a switchover order. For example when wr0, wr1, wr2 have respectively priorities (1, 0, 2),
the primary clock source is wr1. When it dies (e.g. rats ate the fiber), the PLL will automatically
switch to wr1, and if wr1 dies, to wr2. When all the ports are down, the PLL will switch to holdover
mode. In V3, calling this command with negative (priority) removes the port from the locking list.
*/
case HEXP_LOCK_CMD_START:
return hal_port_start_lock(port_name, priority);
/* Check if locked - called by the PTPd repeatedly after calling "Start locking" to check if the
PLL has already locked to and stabilized the reference frequency */
case HEXP_LOCK_CMD_CHECK:
rval = hal_port_check_lock(port_name);
......@@ -50,24 +83,37 @@ int halexp_lock_cmd(const char *port_name, int command, int priority)
break;
}
return -100;
return -100; /* fixme: add real error code */
}
int halexp_query_port(char *port_name, int id)
{
}
/* Phase/Clock adjutsment call. Called by the PTPd servo. Controls both the PLLs and the PPS Generator. */
int halexp_pps_cmd(int cmd, hexp_pps_params_t *params)
{
switch(cmd)
{
/* fixme: TODO: implement HEXP_PPSG_CMD_GET call */
/* Phase adjustment call: adjusts the phase shift between the uplink port (port_name) and the
local VCTCXO clock by adding a number of picoseconds given in (params->adjust_phase_shift) to the
current phase setpoint (i.e. when adjust is positive, the resulting ref clock/PPS goes a bit into
future, it if's negative - it rolls back into past). Note that to have a seamless swictchover,
the phase shifts for different uplinks have to be coherent (i.e. the phase of the uplink clock +
its adjustment shall result in the same VCTCXO phase. Keeping the coherency between the phase
setpoints for different uplinks is the task of the PTPd.*/
case HEXP_PPSG_CMD_ADJUST_PHASE:
shw_dmpll_phase_shift(params->port_name, params->adjust_phase_shift);
return 0;
/* PPS adjustment call, independent for the nanosecond (a.k.a. 8ns cycle) counter and the seconds (UTC)
counter. The counters are adjusted by atomically adding (params->adjust_nsec/utc). Since there's a
single PPS counter, these adjustments are port-independent. Once the coarse (8ns) offset is fixed,
fine adjustments are done with ADJUST_PHASE call, independently for each uplink to accommodate
the different phase shifts on each port (and the fiber/cable connected to it).
*/
case HEXP_PPSG_CMD_ADJUST_NSEC:
shw_pps_gen_adjust_nsec(params->adjust_nsec);
return 0;
......@@ -76,11 +122,22 @@ int halexp_pps_cmd(int cmd, hexp_pps_params_t *params)
shw_pps_gen_adjust_utc(params->adjust_utc);
return 0;
/* Returns non-zero if the PPS/PLL adjustment is in progress.
WARNING: timestamps of the packets sent/received during the PPS counter adjustment are VERY LIKELY
to be broken. Currently, the servo in PTPd just introduces a dumb, 2-second delay after each adjustment,
to make sure following packets will have already their timestamps generated using the updated counter.
fixme: the NIC driver should check the status of the PPS generator adjustment and if it detects
a pending adjustment it shall not timestamp any packets, so the PTPd will simply ignore them during
delay calculation. */
case HEXP_PPSG_CMD_POLL:
return shw_dmpll_shifter_busy(params->port_name) || shw_pps_gen_busy();
}
}
/* PLL debug call, foreseen for live adjustment of some internal PLL parameters (gains, timeouts, etc.)
To be implemented. */
int halexp_pll_cmd(int cmd, hexp_pll_cmd_t *params)
{
}
......@@ -90,6 +147,8 @@ static void hal_cleanup_wripc()
wripc_close(hal_ipc);
}
/* Creates a wripc server and exports all public API functions */
int hal_init_wripc()
{
hal_ipc = wripc_create_server(WRSW_HAL_SERVER_ADDR);
......@@ -114,12 +173,15 @@ int hal_init_wripc()
return 0;
}
/* wripc update function, must be called in the main program loop */
int hal_update_wripc()
{
return wripc_process(hal_ipc);
}
/* Returns 1 if there's already an instance of the HAL running. Used to prevent from
launching multiple HALs simultaneously. */
int hal_check_running()
{
wripc_handle_t fd;
......
......@@ -5,32 +5,36 @@
#define HAL_MAX_PORTS 64
/* Where do we listen for wripc requests */
#define WRSW_HAL_SERVER_ADDR "wrsw_hal"
// checks if the calibration unit is idle
/* Calibration call commands, handled by halexp_calibration_cmd() */
/* checks if the calibration unit is idle */
#define HEXP_CAL_CMD_CHECK_IDLE 1
// enables/disables transmission of calibration pattern
/* enables/disables transmission of calibration patter*/
#define HEXP_CAL_CMD_TX_PATTERN 2
// requests a measurement of TX delta
/* requests the measurement of DeltaTX */
#define HEXP_CAL_CMD_TX_MEASURE 4
// requests a measurement of RX delta
/*requests a measurement of RX delta */
#define HEXP_CAL_CMD_RX_MEASURE 5
// get the raw delta_rx
/* get the raw delta_rx/tx (totally unprocessed - expressed as raw DMTD phase) */
#define HEXP_CAL_CMD_GET_RAW_DELTA_RX 6
// get the raw delta_tx
#define HEXP_CAL_CMD_GET_RAW_DELTA_TX 7
/* Calibration call responses */
/* Calibrator is busy */
#define HEXP_CAL_RESP_BUSY 1
#define HEXP_CAL_RESP_OK 0
#define HEXP_CAL_RESP_ERROR -1
/* Locking call commands/responses */
#define HEXP_LOCK_CMD_START 1
#define HEXP_LOCK_CMD_CHECK 2
......@@ -38,12 +42,14 @@
#define HEXP_LOCK_STATUS_BUSY 1
#define HEXP_LOCK_STATUS_NONE 2
/* PPS Generator commands */
#define HEXP_PPSG_CMD_GET 0
#define HEXP_PPSG_CMD_ADJUST_PHASE 1
#define HEXP_PPSG_CMD_ADJUST_UTC 2
#define HEXP_PPSG_CMD_ADJUST_NSEC 3
#define HEXP_PPSG_CMD_POLL 4
/* Foreseen for halexp_pll_cmd() */
#define HEXP_ON 1
#define HEXP_OFF 0
......@@ -53,42 +59,54 @@
#define HEXP_FREQ 0
#define HEXP_PHASE 1
/* Port modes (hexp_port_state_t.mode) */
#define HEXP_PORT_MODE_WR_M_AND_S 4
#define HEXP_PORT_MODE_WR_MASTER 1
#define HEXP_PORT_MODE_WR_SLAVE 2
#define HEXP_PORT_MODE_NON_WR 3
/* External clock source control commands (temporary) */
#define HEXP_EXTSRC_CMD_CHECK 0
#define HEXP_EXTSRC_STATUS_LOCKED 0
#define HEXP_EXTSRC_STATUS_NOSRC 2
/* Number of the fractional bits in the fiber alpha coefficient - used by the PTPd servo to
avoid floating point calculations. */
#define FIX_ALPHA_FRACBITS 40
/* PPS adjustment call parameter */
typedef struct {
/* Port to be adjusted (only relevant for phase adjustment - i.e. HEXP_PPSG_CMD_ADJUST_PHASE */
char port_name[16];
/* Current value of the phase shift on port (port_name) */
uint32_t current_phase_shift;
/* Phase shift adjustment in picoseconds. Positive = future, negative = past */
int32_t adjust_phase_shift;
/* UTC/Cycle counter adjustment (seconds/nanoseconds) */
int64_t adjust_utc;
int32_t adjust_nsec;
int32_t adjust_nsec;
/* Current time obtained by PPSG_GET call */
uint64_t current_utc;
uint32_t current_nsec;
} hexp_pps_params_t;
/* Port modes (hexp_port_state_t.mode) */
#define HEXP_PORT_MODE_WR_MASTER 1
#define HEXP_PORT_MODE_WR_SLAVE 2
#define HEXP_PORT_MODE_NON_WR 3
#define FIX_ALPHA_FRACBITS 40
/*
#define HEXP_PORT_TSC_RISING 1
#define HEXP_PORT_TSC_FALLING 2
*/
/* Generic port state structure, filled in by halexp_get_port_state() */
typedef struct {
/* When non-zero: port state is valid */
int valid;
/* WR-PTP role of the port (Master, Slave, etc.) */
/* WR-PTP role of the port (Master, Slave, non-WR, etc.) */
int mode;
/* TX and RX delays (combined, big Deltas from the link model in the spec) */
/* TX and RX delays in picoseconds (combined, big Deltas from the link model in the spec). */
uint32_t delta_tx;
uint32_t delta_rx;
......@@ -106,35 +124,62 @@ typedef struct {
/* When non-zero: RX path is calibrated (delta_rx contains valid value) */
int rx_calibrated;
int tx_tstamp_counter;
int rx_tstamp_counter;
/* When non-zero: port is locked (i.e. the PLL is currently using it as a reference source) */
int is_locked;
/* Lock priority of the port. (0 = highest, 1 = sligtly smaller, etc).
Negative = the PLL will not use the port for clock recovery. */
int lock_priority;
// timestamp linearization paramaters
uint32_t phase_setpoint; // DMPLL phase setpoint (picoseconds)
/* Current PLL phase setpoint (picoseconds). If lock_priority < 0, it's irrelevant. */
uint32_t phase_setpoint;
uint32_t clock_period; // reference lock period in picoseconds
uint32_t t2_phase_transition; // approximate DMTD phase value (on slave port) at which RX timestamp (T2) counter transistion occurs (picoseconds)
/* Reference clock period in picoseconds (8000 for WR). */
uint32_t clock_period;
uint32_t t4_phase_transition; // approximate phase value (on master port) at which RX timestamp (T4) counter transistion occurs (picoseconds)
/* Approximate DMTD phase value (on a slave port) at which RX timestamp (T2) counter
transistion occurs (picoseconds). Used to merge the timestamp counter value with the phase value
to obtain a picosecond-level t2 timestamp. See linearize_rx_timestamp() function in libptpnetif and
Tom's M.Sc for the details of the algorithm. */
uint32_t t2_phase_transition;
/* The same, but for a master port. In V3 these will be identical, as there will be no physical differences
between the uplink and downlink ports */
uint32_t t4_phase_transition;
/* MAC address of the port */
uint8_t hw_addr[6];
/* Hardware index of the port */
int hw_index;
/* Fixed-point fiber alpha parameter, used by the PTPd to calculate the asymmetry of the fiber.
Relevant only for ports working in Slave mode.
WARNING! This is not the alpha as defined in the WR protocol specification, but a fixed point version
(which is NOT EXACTLY a floating point alpha from the spec directly converted to a fixed format by
shifting and rounding). The actual equation for obtaining fix_alpha from alpha is in hal_ports.c */
int32_t fiber_fix_alpha;
} hexp_port_state_t;
/* Port list structure. Filled in by halexp_get_port_state() */
typedef struct {
int num_ports;
/* number of ports in port_names[][] */
int num_ports;
/* array of port names (Linux network I/Fs) */
char port_names[HAL_MAX_PORTS][16];
} hexp_port_list_t;
/* PLL command structure for halexp_pll_cmd(). To be upgraded with more parameters in the V3 */
typedef struct {
int ki, kp;
int pll;
int branch;
} hexp_pll_cmd_t;
int halexp_check_running();
......
/* Main HAL file */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
......@@ -17,9 +19,13 @@
#define MAX_CLEANUP_CALLBACKS 16
#define assert_init(proc) { int ret; if((ret = proc) < 0) return ret; }
static int daemon_mode= 0;
static hal_cleanup_callback_t cleanup_cb[MAX_CLEANUP_CALLBACKS];
/* Adds a function to be called during the HAL shutdown. */
int hal_add_cleanup_callback(hal_cleanup_callback_t cb)
{
int i;
......@@ -33,6 +39,7 @@ int hal_add_cleanup_callback(hal_cleanup_callback_t cb)
return -1;
}
/* Calls all cleanup callbacks */
static void call_cleanup_cbs()
{
int i;
......@@ -43,17 +50,19 @@ static void call_cleanup_cbs()
}
/* Determines which FPGA bitstreams shall be loaded */
int hal_setup_fpga_images()
{
char fpga_dir[128];
char fw_name[128];
/* query the path to the firmware directory in the config file */
if( hal_config_get_string("global.hal_firmware_path", fpga_dir, sizeof(fpga_dir)) < 0)
return -1;
// shw_fpga_force_firmware_reload();
shw_set_fpga_firmware_path(fpga_dir);
/* check if the config demands a particular bitstream (otherwise libswitchhw will load the default ones) */
if( !hal_config_get_string("global.main_firmware", fw_name, sizeof(fw_name)))
shw_request_fpga_firmware(FPGA_ID_MAIN, fw_name);
......@@ -64,6 +73,7 @@ int hal_setup_fpga_images()
}
/* loads (load = 1) or unloads (load = 0) a WR kernel module (name). */
static int load_unload_kmod(const char *name, int load)
{
static char modules_path[128];
......@@ -88,9 +98,9 @@ static int load_unload_kmod(const char *name, int load)
return 0;
}
#define assert_init(proc) { int ret; if((ret = proc) < 0) return ret; }
/* Unloads all WR kernel modules during the shutdown */
static void unload_kernel_modules()
{
char module_name[80];
......@@ -106,6 +116,7 @@ static void unload_kernel_modules()
}
/* Loads all WR kernel modules specified in the HAL config file */
int hal_load_kernel_modules()
{
char module_name[80];
......@@ -141,6 +152,7 @@ int hal_shutdown()
return 0;
}
/* Main initialization function */
int hal_init()
{
int enable;
......@@ -150,25 +162,38 @@ int hal_init()
memset(cleanup_cb, 0, sizeof(cleanup_cb));
/* Set up trap for some signals - the main purpose is to prevent the hardware from working
when the HAL is shut down - launching the HAL on already initialized HW will freeze the system. */
signal(SIGSEGV, sighandler);
signal(SIGINT, sighandler);
signal(SIGTERM, sighandler);
signal(SIGILL, sighandler);
/* parse the configuration file and choose the bitstreams to load to the FPGAs */
assert_init(hal_parse_config());
assert_init(hal_setup_fpga_images());
/* Check if the switch will operate as a grandmaster and eventually enable the ext clock input
prior to initializing libswitchhw. */
if(!hal_config_get_int("timing.use_external_clock", &enable))
shw_use_external_reference(enable);
/* Perform a low-level hardware init, load bitstreams, initialize non-kernel drivers */
assert_init(shw_init());
/* Load kernel drivers */
assert_init(hal_load_kernel_modules());
/* Initialize port FSMs - see hal_ports.c */
assert_init(hal_init_ports());
/* Create a WRIPC server for HAL public API */
assert_init(hal_init_wripc());
return 0;
return 0;
}
/* Main loop update - polls for WRIPC requests and rolls the port state machines */
void hal_update()
{
hal_update_wripc();
......@@ -177,6 +202,7 @@ void hal_update()
usleep(1000);
}
/* Turns a nice and well-behaving HAL into an evil servant of satan. */
void hal_deamonize()
{
pid_t pid, sid;
......@@ -264,7 +290,7 @@ void hal_parse_cmdline(int argc, char *argv[])
int main(int argc, char *argv[])
{
/* Prevent from running HAL twice - this will likely freeze the system */
if(hal_check_running())
{
fprintf(stderr, "Fatal: There is another WR HAL instance running. We can't work together.\n\n");
......@@ -273,12 +299,11 @@ int main(int argc, char *argv[])
hal_parse_cmdline(argc, argv);
hal_init();
hal_init();
if(daemon_mode)
hal_deamonize();
for(;;) hal_update();
hal_shutdown();
......
This diff is collapsed.
......@@ -61,7 +61,8 @@ typedef struct {
/* Fiber "alpha" asymmetry coefficient, as defined in the WRPTP Specification */
double fiber_alpha;
/* Fixed point fiber asymmetry coefficient. Expressed as (2 ^ 40 * (fiber_alpha - 1)). */ int32_t fiber_fix_alpha;
/* Fixed point fiber asymmetry coefficient. Expressed as (2 ^ 40 * (fiber_alpha - 1)). */
int32_t fiber_fix_alpha;
/* When non-zero: RX path is calibrated (delta_*_rx contain valid values) */
int rx_calibrated;
......@@ -74,7 +75,6 @@ typedef struct {
int hal_parse_config();
int hal_check_running();
int hal_config_get_int(const char *name, int *value);
int hal_config_get_double(const char *name, double *value);
int hal_config_get_string(const char *name, char *value, int max_len);
......
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