Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
F
FMC DEL 1ns 4cha
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
2
Issues
2
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
Wiki
Wiki
image/svg+xml
Discourse
Discourse
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
Projects
FMC DEL 1ns 4cha
Commits
e9d5e03d
Commit
e9d5e03d
authored
Jun 05, 2012
by
Tomasz Wlostowski
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
software: bugfixes in raw readout mode (enabled by default)
parent
48385a0a
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
114 additions
and
125 deletions
+114
-125
fd_channel_regs.h
software/include/fd_channel_regs.h
+3
-0
fd_main_regs.h
software/include/fd_main_regs.h
+22
-34
fdelay_lib.h
software/include/fdelay_lib.h
+3
-0
fdelay_lib.c
software/lib/fdelay_lib.c
+38
-44
spec_common.c
software/lib/spec_common.c
+48
-47
No files found.
software/include/fd_channel_regs.h
View file @
e9d5e03d
...
...
@@ -57,6 +57,9 @@
/* definitions for field: Disable Fine Part update in reg: Delay Control Register */
#define FD_DCR_NO_FINE WBGEN2_GEN_MASK(7, 1)
/* definitions for field: Disable Fine Part update in reg: Delay Control Register */
#define FD_DCR_FORCE_HI WBGEN2_GEN_MASK(8, 1)
/* definitions for register: Fine Range Register */
/* definitions for register: Pulse start time / offset (MSB TAI seconds) */
...
...
software/include/fd_main_regs.h
View file @
e9d5e03d
...
...
@@ -3,7 +3,7 @@
* File : fd_main_regs.h
* Author : auto-generated by wbgen2 from fd_main_wishbone_slave.wb
* Created : Mon
May 21 20:09:5
0 2012
* Created : Mon
Jun 4 13:42:2
0 2012
* Standard : ANSI C
THIS FILE WAS GENERATED BY wbgen2 FROM SOURCE FILE fd_main_wishbone_slave.wb
...
...
@@ -135,7 +135,7 @@
/* definitions for field: PPS Calibration output enable in reg: Calibration register */
#define FD_CALR_CAL_PPS WBGEN2_GEN_MASK(1, 1)
/* definitions for field:
Triggers calibration pulses
in reg: Calibration register */
/* definitions for field:
Produce DDMTD calibration pattern
in reg: Calibration register */
#define FD_CALR_CAL_DMTD WBGEN2_GEN_MASK(2, 1)
/* definitions for field: Enable pulse generation in reg: Calibration register */
...
...
@@ -144,39 +144,27 @@
#define FD_CALR_PSEL_W(value) WBGEN2_GEN_WRITE(value, 3, 4)
#define FD_CALR_PSEL_R(reg) WBGEN2_GEN_READ(reg, 3, 4)
/* definitions for field: DMTD Feedback Channel Select in reg: Calibration register */
#define FD_CALR_DMTD_FBSEL WBGEN2_GEN_MASK(7, 1)
/* definitions for field: DMTD Tag in reg: Calibration register */
#define FD_CALR_DMTD_TAG_MASK WBGEN2_GEN_MASK(8, 23)
#define FD_CALR_DMTD_TAG_SHIFT 8
#define FD_CALR_DMTD_TAG_W(value) WBGEN2_GEN_WRITE(value, 8, 23)
#define FD_CALR_DMTD_TAG_R(reg) WBGEN2_GEN_READ(reg, 8, 23)
/* definitions for field: DMTD Tag Ready in reg: Calibration register */
#define FD_CALR_DMTD_TAG_RDY WBGEN2_GEN_MASK(31, 1)
/* definitions for register: Softpll Register */
/* definitions for register: DMTD Input Tag Register */
/* definitions for field:
Frequency/Phase tag in reg: Softpll
Register */
#define FD_
SPLLR_TAG_MASK WBGEN2_GEN_MASK(0, 20
)
#define FD_
SPLLR_TAG_SHIFT
0
#define FD_
SPLLR_TAG_W(value) WBGEN2_GEN_WRITE(value, 0, 20
)
#define FD_
SPLLR_TAG_R(reg) WBGEN2_GEN_READ(reg, 0, 20
)
/* definitions for field:
DMTD Tag in reg: DMTD Input Tag
Register */
#define FD_
DMTR_IN_TAG_MASK WBGEN2_GEN_MASK(0, 31
)
#define FD_
DMTR_IN_TAG_SHIFT
0
#define FD_
DMTR_IN_TAG_W(value) WBGEN2_GEN_WRITE(value, 0, 31
)
#define FD_
DMTR_IN_TAG_R(reg) WBGEN2_GEN_READ(reg, 0, 31
)
/* definitions for field:
Tag Ready in reg: Softpll
Register */
#define FD_
SPLLR_TAG_RDY WBGEN2_GEN_MASK(20
, 1)
/* definitions for field:
DMTD Tag Ready in reg: DMTD Input Tag
Register */
#define FD_
DMTR_IN_RDY WBGEN2_GEN_MASK(31
, 1)
/* definitions for field: Freq/Phase mode select in reg: Softpll Register */
#define FD_SPLLR_MODE WBGEN2_GEN_MASK(21, 1)
/* definitions for register: DMTD Output Tag Register */
/* definitions for register: Softpll DAC Register */
/* definitions for field: DMTD Tag in reg: DMTD Output Tag Register */
#define FD_DMTR_OUT_TAG_MASK WBGEN2_GEN_MASK(0, 31)
#define FD_DMTR_OUT_TAG_SHIFT 0
#define FD_DMTR_OUT_TAG_W(value) WBGEN2_GEN_WRITE(value, 0, 31)
#define FD_DMTR_OUT_TAG_R(reg) WBGEN2_GEN_READ(reg, 0, 31)
/* definitions for field: DAC Value in reg: Softpll DAC Register */
#define FD_SDACR_DAC_VAL_MASK WBGEN2_GEN_MASK(0, 16)
#define FD_SDACR_DAC_VAL_SHIFT 0
#define FD_SDACR_DAC_VAL_W(value) WBGEN2_GEN_WRITE(value, 0, 16)
#define FD_SDACR_DAC_VAL_R(reg) WBGEN2_GEN_READ(reg, 0, 16)
/* definitions for field: DMTD Tag Ready in reg: DMTD Output Tag Register */
#define FD_DMTR_OUT_RDY WBGEN2_GEN_MASK(31, 1)
/* definitions for register: Acam to Delay line fractional part Scale Factor Register */
...
...
@@ -417,10 +405,10 @@
#define FD_REG_TDCSR 0x00000020
/* [0x24]: REG Calibration register */
#define FD_REG_CALR 0x00000024
/* [0x28]: REG
Softpll
Register */
#define FD_REG_
SPLLR
0x00000028
/* [0x2c]: REG
Softpll DAC
Register */
#define FD_REG_
SDACR
0x0000002c
/* [0x28]: REG
DMTD Input Tag
Register */
#define FD_REG_
DMTR_IN
0x00000028
/* [0x2c]: REG
DMTD Output Tag
Register */
#define FD_REG_
DMTR_OUT
0x0000002c
/* [0x30]: REG Acam to Delay line fractional part Scale Factor Register */
#define FD_REG_ADSFR 0x00000030
/* [0x34]: REG Acam Timestamp Merging Control Register */
...
...
software/include/fdelay_lib.h
View file @
e9d5e03d
...
...
@@ -42,6 +42,7 @@ typedef struct {
int32_t
start_offset
;
int32_t
subcycle_offset
;
int32_t
frac
;
uint32_t
tsbcr
;
}
fdelay_raw_time_t
;
typedef
struct
...
...
@@ -128,4 +129,6 @@ void fdelay_set_user_offset(fdelay_device_t *dev,int input, int64_t offset);
int
fdelay_get_time
(
fdelay_device_t
*
dev
,
fdelay_time_t
*
t
);
int
fdelay_set_time
(
fdelay_device_t
*
dev
,
const
fdelay_time_t
t
);
int
fdelay_dmtd_calibration
(
fdelay_device_t
*
dev
);
#endif
software/lib/fdelay_lib.c
View file @
e9d5e03d
...
...
@@ -26,9 +26,6 @@
#include "fdelay_lib.h"
#include "fdelay_private.h"
#include "spll_defs.h"
#include "spll_common.h"
#include "spll_helper.h"
#include "onewire.h"
...
...
@@ -299,13 +296,14 @@ static int sgpio_init(fdelay_device_t *dev)
if
(
mcp_read
(
dev
,
MCP_IPOL
)
!=
0
)
failed
=
1
;
if
(
failed
)
fail
(
TEST_SPI
,
"Failed to access MCP23S17. Broken SPI connection?"
);
return
failed
?
-
1
:
0
;
}
/* Sets the direction (0 = input, non-zero = output) of a particular MCP23S17 GPIO pin */
static
void
sgpio_set_dir
(
fdelay_device_t
*
dev
,
int
pin
,
int
dir
)
void
sgpio_set_dir
(
fdelay_device_t
*
dev
,
int
pin
,
int
dir
)
{
uint8_t
iodir
=
(
MCP_IODIR
)
+
(
pin
&
0x100
?
1
:
0
);
uint8_t
x
;
...
...
@@ -318,7 +316,7 @@ static void sgpio_set_dir(fdelay_device_t *dev, int pin, int dir)
}
/* Sets the value on a given MCP23S17 GPIO pin */
static
void
sgpio_set_pin
(
fdelay_device_t
*
dev
,
int
pin
,
int
val
)
void
sgpio_set_pin
(
fdelay_device_t
*
dev
,
int
pin
,
int
val
)
{
uint8_t
gpio
=
(
MCP_OLAT
)
+
(
pin
&
0x100
?
1
:
0
);
uint8_t
x
;
...
...
@@ -871,7 +869,7 @@ int fdelay_init(fdelay_device_t *dev)
dev
->
priv_fd
=
(
void
*
)
hw
;
hw
->
raw_mode
=
0
;
hw
->
raw_mode
=
1
;
hw
->
base_addr
=
dev
->
base_addr
;
hw
->
base_i2c
=
0x100
;
hw
->
base_onewire
=
dev
->
base_addr
+
0x500
;
...
...
@@ -914,8 +912,8 @@ int fdelay_init(fdelay_device_t *dev)
hw
->
calib
.
frr_poly
[
1
]
=
-
29825595LL
;
hw
->
calib
.
frr_poly
[
2
]
=
3801939743082LL
;
hw
->
calib
.
tdc_zero_offset
=
35600
;
hw
->
calib
.
atmcr_val
=
4
|
(
1500
<<
4
);
//
hw->calib.atmcr_val = 2 | (1000 << 4);
//
hw->calib.atmcr_val = 4 | (1500 << 4);
hw
->
calib
.
atmcr_val
=
2
|
(
1000
<<
4
);
hw
->
calib
.
adsfr_val
=
56648
;
hw
->
calib
.
acam_start_offset
=
10000
;
for
(
i
=
0
;
i
<
4
;
i
++
)
...
...
@@ -934,8 +932,6 @@ int fdelay_init(fdelay_device_t *dev)
if
(
ad9516_init
(
dev
)
<
0
)
return
-
1
;
if
(
ds18x_init
(
dev
)
<
0
)
{
fail
(
TEST_SPI
,
"DS18x sensor not detected."
);
...
...
@@ -1053,6 +1049,7 @@ fdelay_time_t fdelay_from_picos(const uint64_t ps)
fdelay_time_t
t
;
uint64_t
tmp
=
ps
;
t
.
frac
=
(
tmp
%
8000ULL
)
*
(
uint64_t
)(
1
<<
FDELAY_FRAC_BITS
)
/
8000ULL
;
tmp
-=
(
tmp
%
8000ULL
);
tmp
/=
8000ULL
;
...
...
@@ -1119,11 +1116,12 @@ static fdelay_time_t ts_add_ps(fdelay_time_t a, int64_t b)
return
ts_add
(
a
,
fdelay_from_picos
(
b
));
}
static
int
poll_rbuf
(
fdelay_device_t
*
dev
)
static
int
poll_rbuf
(
fdelay_device_t
*
dev
,
uint32_t
*
o_tsbcr
)
{
fd_decl_private
(
dev
)
uint32_t
tsbcr
=
fd_readl
(
FD_REG_TSBCR
);
if
(
o_tsbcr
)
*
o_tsbcr
=
tsbcr
;
// fprintf(stderr,"Count %d empty %d\n", FD_TSBCR_COUNT_R(tsbcr), tsbcr & FD_TSBCR_EMPTY ? 1 : 0);
if
((
tsbcr
&
FD_TSBCR_EMPTY
)
==
0
)
...
...
@@ -1164,6 +1162,14 @@ fdelay_time_t ts_normalize(fdelay_time_t denorm)
return
denorm
;
}
static
int
sign_extend
(
int
n
,
int
nbits
)
{
if
(
n
&
(
1
<<
(
nbits
-
1
)))
return
n
|
(
~
((
1
<<
nbits
)
-
1
));
else
return
n
;
}
/* as in VHDL */
void
ts_postprocess
(
fdelay_device_t
*
dev
,
fdelay_time_t
*
t
)
{
...
...
@@ -1172,15 +1178,19 @@ void ts_postprocess(fdelay_device_t *dev, fdelay_time_t *t)
t
->
utc
=
t
->
raw
.
utc
;
if
(
t
->
raw
.
start_offset
<=
FD_ATMCR_C_THR_R
(
hw
->
calib
.
atmcr_val
)
&&
(
post_frac_start_adj
>
FD_ATMCR_F_THR_R
(
hw
->
calib
.
atmcr_val
)))
int
c_thr
=
FD_ATMCR_C_THR_R
(
hw
->
calib
.
atmcr_val
);
int
f_thr
=
FD_ATMCR_F_THR_R
(
hw
->
calib
.
atmcr_val
);
// printf("CThr: %d FThr: %d\n", c_thr, f_thr);
if
(
t
->
raw
.
start_offset
<=
c_thr
&&
(
post_frac_start_adj
>
f_thr
))
t
->
coarse
=
(
t
->
raw
.
coarse
-
1
)
*
16
;
else
t
->
coarse
=
(
t
->
raw
.
coarse
)
*
16
;
int64_t
post_frac_multiplied
=
post_frac_start_adj
*
(
int64_t
)
hw
->
calib
.
adsfr_val
;
t
->
coarse
+=
t
->
raw
.
subcycle_offset
t
->
coarse
+=
sign_extend
(
t
->
raw
.
subcycle_offset
,
5
)
+
(
post_frac_multiplied
>>
24
);
t
->
frac
=
(
post_frac_multiplied
>>
12
)
&
0xfff
;
...
...
@@ -1192,11 +1202,11 @@ void ts_postprocess(fdelay_device_t *dev, fdelay_time_t *t)
int
fdelay_read
(
fdelay_device_t
*
dev
,
fdelay_time_t
*
timestamps
,
int
how_many
)
{
fd_decl_private
(
dev
)
uint32_t
tsbcr
;
int
n_read
=
0
;
// dbg("tsbcr %x\n", fd_readl(FD_REG_TSBCR));
while
(
poll_rbuf
(
dev
))
while
(
poll_rbuf
(
dev
,
&
tsbcr
))
{
fdelay_time_t
ts
;
uint32_t
seq_frac
;
...
...
@@ -1204,12 +1214,15 @@ int fdelay_read(fdelay_device_t *dev, fdelay_time_t *timestamps, int how_many)
if
(
hw
->
raw_mode
)
{
fd_writel
(
FD_TSBR_ADVANCE_ADV
,
FD_REG_TSBR_ADVANCE
);
uint32_t
cyc
,
dbg
;
ts
.
raw
.
utc
=
((
int64_t
)
(
fd_readl
(
FD_REG_TSBR_SECH
)
&
0xff
)
<<
32
)
|
fd_readl
(
FD_REG_TSBR_SECL
);
cyc
=
fd_readl
(
FD_REG_TSBR_CYCLES
)
&
0xfffffff
;
ts
.
raw
.
coarse
=
cyc
>>
4
;
ts
.
raw
.
start_offset
=
cyc
&
0xf
;
dbg
=
fd_readl
(
FD_REG_TSBR_DEBUG
);
seq_frac
=
fd_readl
(
FD_REG_TSBR_FID
);
...
...
@@ -1227,12 +1240,15 @@ int fdelay_read(fdelay_device_t *dev, fdelay_time_t *timestamps, int how_many)
}
else
{
fd_writel
(
FD_TSBR_ADVANCE_ADV
,
FD_REG_TSBR_ADVANCE
);
ts
.
utc
=
((
int64_t
)
(
fd_readl
(
FD_REG_TSBR_SECH
)
&
0xff
)
<<
32
)
|
fd_readl
(
FD_REG_TSBR_SECL
);
ts
.
coarse
=
fd_readl
(
FD_REG_TSBR_CYCLES
)
&
0xfffffff
;
// dbg("Coarse %d\n", ts.coarse);
seq_frac
=
fd_readl
(
FD_REG_TSBR_FID
);
ts
.
frac
=
FD_TSBR_FID_FINE_R
(
seq_frac
);
ts
.
seq_id
=
FD_TSBR_FID_SEQID_R
(
seq_frac
);
ts
.
raw
.
tsbcr
=
tsbcr
;
// ts.channel = FD_TSBR_FID_CHANNEL_R(seq_frac);
}
...
...
@@ -1264,7 +1280,7 @@ int fdelay_configure_output(fdelay_device_t *dev, int channel, int enable, int64
delta
=
fdelay_from_picos
(
delta_ps
);
printf
(
"Start: %lld: %d:%d rep %d
\n
"
,
start
.
utc
,
start
.
coarse
,
start
.
frac
,
rep_count
);
//
printf("Start: %lld: %d:%d rep %d\n", start.utc, start.coarse, start.frac, rep_count);
chan_writel
(
hw
->
frr_cur
[
channel
-
1
],
FD_REG_FRR
);
...
...
@@ -1316,11 +1332,14 @@ int fdelay_configure_pulse_gen(fdelay_device_t *dev, int channel, int enable, fd
end
=
ts_add_ps
(
t_start
,
hw
->
output_user_offset
+
width_ps
-
4000
);
delta
=
fdelay_from_picos
(
delta_ps
);
#if 0
printf("Channel: %d\n",channel);
printf("TStart: %d: %d:%d rep %d\n", t_start.utc, t_start.coarse, t_start.frac, rep_count);
printf("Start: %d: %d:%d rep %d\n", start.utc, start.coarse, start.frac, rep_count);
printf("End: %d: %d:%d rep %d\n", end.utc, end.coarse, end.frac, rep_count);
printf("Delta: %d: %d:%d rep %d\n", delta.utc, delta.coarse, delta.frac, rep_count);
#endif
chan_writel
(
hw
->
frr_cur
[
channel
-
1
],
FD_REG_FRR
);
...
...
@@ -1455,28 +1474,3 @@ int fdelay_dbg_sync_lost(fdelay_device_t *dev)
return
(
fd_readl
(
FD_REG_EIC_ISR
)
&
FD_EIC_ISR_SYNC_STATUS
)
?
1
:
0
;
}
# if 0
/* We might implement SPLL-based DMTD calibration, but not now - don't include in the driver */
int
fd_update_spll
(
fdelay_device_t
*
dev
)
{
struct
spll_helper_state
pll
;
fd_decl_private
(
dev
)
int
i
=
0
;
helper_start
(
dev
,
&
pll
);
fd_writel
(
FD_CALR_CAL_DMTD
,
FD_REG_CALR
);
sgpio_set_pin
(
dev
,
SGPIO_TRIG_SEL
,
0
);
for
(;;)
{
helper_update
(
&
pll
);
//if(pll.prelock.ld.locked)
// dbg("LOCK!");
}
}
#endif
software/lib/spec_common.c
View file @
e9d5e03d
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <stdint.h>
#include <getopt.h>
#include "spec/speclib.h"
#include "fdelay_lib.h"
void
spec_writel
(
void
*
priv
,
uint32_t
data
,
uint32_t
addr
)
void
loader_low_level
(){};
/* fixme: include the kernel file */
static
void
fd_spec_writel
(
void
*
priv
,
uint32_t
data
,
uint32_t
addr
)
{
*
(
volatile
uint32_t
*
)(
priv
+
addr
)
=
data
;
spec_writel
(
priv
,
data
,
addr
);
}
uint32_t
spec_readl
(
void
*
priv
,
uint32_t
addr
)
static
uint32_t
fd_
spec_readl
(
void
*
priv
,
uint32_t
addr
)
{
return
*
(
volatile
uint32_t
*
)(
priv
+
addr
);
return
spec_readl
(
priv
,
addr
);
}
void
*
map_spec
(
int
bus
,
int
dev
)
int
spec_fdelay_init_bd
(
fdelay_device_t
*
dev
,
int
bus
,
int
dev_fn
,
uint32_t
base
)
{
char
path
[
1024
];
int
fd
;
void
*
ptr
;
uint64_t
base
;
snprintf
(
path
,
sizeof
(
path
),
"/sys/bus/pci/drivers/spec/0000:%02x:%02x.0/resource"
,
bus
,
dev
);
FILE
*
f
=
fopen
(
path
,
"r"
);
fscanf
(
f
,
"0x%llx"
,
&
base
);
printf
(
"raw base addr: %llx
\n
"
,
base
);
dev
->
priv_io
=
spec_open
(
bus
,
dev_fn
);
fd
=
open
(
"/dev/mem"
,
O_SYNC
|
O_RDWR
);
if
(
fd
<=
0
)
if
(
!
dev
->
priv_io
)
{
perror
(
"open"
);
return
NULL
;
fprintf
(
stderr
,
"Can't map the SPEC @ %x:%x
\n
"
,
bus
,
dev_fn
);
return
-
1
;
}
ptr
=
mmap
(
NULL
,
0x100000
,
PROT_READ
|
PROT_WRITE
,
MAP_SHARED
,
fd
,
(
void
*
)
base
);
if
((
int
)
ptr
==
-
1
)
{
perror
(
"mmap"
);
close
(
fd
);
return
NULL
;
}
dev
->
writel
=
fd_spec_writel
;
dev
->
readl
=
fd_spec_readl
;
dev
->
base_addr
=
base
;
return
ptr
;
}
spec_vuart_init
(
dev
->
priv_io
,
0xe0500
);
/* for communication with WRCore during DMTD calibration */
if
(
fdelay_init
(
dev
)
<
0
)
return
-
1
;
return
0
;
}
int
spec_fdelay_init
(
fdelay_device_t
*
dev
,
int
pbus
,
int
pdev
)
int
spec_fdelay_init
(
fdelay_device_t
*
dev
,
int
argc
,
char
*
argv
[]
)
{
dev
->
priv_io
=
map_spec
(
pbus
,
pdev
);
int
bus
=
-
1
,
dev_fn
=
-
1
,
c
;
uint32_t
base
=
0x80000
;
if
(
!
dev
->
priv_io
)
while
((
c
=
getopt
(
argc
,
argv
,
"b:d:f:"
))
!=
-
1
)
{
fprintf
(
stderr
,
"Can't map the SPEC @ %x:%x
\n
"
,
pbus
,
pdev
);
switch
(
c
)
{
case
'b'
:
sscanf
(
optarg
,
"%i"
,
&
bus
);
break
;
case
'd'
:
sscanf
(
optarg
,
"%i"
,
&
dev_fn
);
break
;
case
'u'
:
sscanf
(
optarg
,
"%i"
,
&
base
);
break
;
default:
fprintf
(
stderr
,
"Use:
\"
%s [-b bus] [-d devfn] [-u Fine Delay base] [-k]
\"\n
"
,
argv
[
0
]);
fprintf
(
stderr
,
"By default, the first available SPEC is used and the FD is assumed at 0x%x.
\n
"
,
base
);
return
-
1
;
}
}
dev
->
writel
=
spec_writel
;
dev
->
readl
=
spec_readl
;
dev
->
base_addr
=
0x80000
;
return
spec_fdelay_init_bd
(
dev
,
bus
,
dev_fn
,
base
);
}
if
(
fdelay_init
(
dev
)
<
0
)
return
-
1
;
return
0
;
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment