Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
O
OpenXG-RAN
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
alex037yang
OpenXG-RAN
Commits
9e28189d
Commit
9e28189d
authored
Jan 27, 2016
by
kaltenbe
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
PLL Configuration
parent
c9780626
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
45 additions
and
40 deletions
+45
-40
cmake_targets/CMakeLists.txt
cmake_targets/CMakeLists.txt
+6
-1
targets/ARCH/SODERA/USERSPACE/LIB/sodera_lib.cpp
targets/ARCH/SODERA/USERSPACE/LIB/sodera_lib.cpp
+39
-39
No files found.
cmake_targets/CMakeLists.txt
View file @
9e28189d
...
@@ -441,15 +441,20 @@ elseif (${RF_BOARD} STREQUAL "OAI_SODERA")
...
@@ -441,15 +441,20 @@ elseif (${RF_BOARD} STREQUAL "OAI_SODERA")
include_directories
(
"
${
OPENAIR_TARGETS
}
/ARCH/SODERA/USERSPACE/LIB"
)
include_directories
(
"
${
OPENAIR_TARGETS
}
/ARCH/SODERA/USERSPACE/LIB"
)
include_directories
(
"
${
OPENAIR_TARGETS
}
/ARCH/SODERA/USERSPACE/LIB/lms7suite/src/lms7002m"
)
include_directories
(
"
${
OPENAIR_TARGETS
}
/ARCH/SODERA/USERSPACE/LIB/lms7suite/src/lms7002m"
)
include_directories
(
"
${
OPENAIR_TARGETS
}
/ARCH/SODERA/USERSPACE/LIB/lms7suite/src/Si5351C"
)
include_directories
(
"
${
OPENAIR_TARGETS
}
/ARCH/SODERA/USERSPACE/LIB/lms7suite/src/Si5351C"
)
include_directories
(
"
${
OPENAIR_TARGETS
}
/ARCH/SODERA/USERSPACE/LIB/lms7suite/src/LMS_StreamBoard"
)
set
(
HW_SOURCE
${
HW_SOURCE
}
set
(
HW_SOURCE
${
HW_SOURCE
}
${
OPENAIR_TARGETS
}
/ARCH/SODERA/USERSPACE/LIB/sodera_lib.cpp
${
OPENAIR_TARGETS
}
/ARCH/SODERA/USERSPACE/LIB/sodera_lib.cpp
)
)
LINK_DIRECTORIES
(
"
${
OPENAIR_TARGETS
}
/ARCH/SODERA/USERSPACE/LIB/lms7suite/src/lms7002m"
)
LINK_DIRECTORIES
(
"
${
OPENAIR_TARGETS
}
/ARCH/SODERA/USERSPACE/LIB/lms7suite/src/lms7002m"
)
LINK_DIRECTORIES
(
"
${
OPENAIR_TARGETS
}
/ARCH/SODERA/USERSPACE/LIB/lms7suite/src/Si5351C"
)
LINK_DIRECTORIES
(
"
${
OPENAIR_TARGETS
}
/ARCH/SODERA/USERSPACE/LIB/lms7suite/src/Si5351C"
)
LINK_DIRECTORIES
(
"
${
OPENAIR_TARGETS
}
/ARCH/SODERA/USERSPACE/LIB/lms7suite/src/LMS_StreamBoard"
)
LINK_DIRECTORIES
(
"
${
OPENAIR_TARGETS
}
/ARCH/SODERA/USERSPACE/LIB/lms7suite/src/kissFFT"
)
LINK_DIRECTORIES
(
"/usr/lib/x86_64-linux-gnu"
)
LINK_DIRECTORIES
(
"/usr/lib/x86_64-linux-gnu"
)
set
(
option_HW_lib
"usb-1.0"
)
set
(
option_HW_lib
"usb-1.0"
)
set
(
LMS7002_LIB
"libLMS7002M.a"
)
set
(
LMS7002_LIB
"libLMS7002M.a"
)
set
(
Si5351C_LIB
"libSi5351C.a"
)
set
(
Si5351C_LIB
"libSi5351C.a"
)
set
(
LMSStreamBoard_LIB
"libLMS_StreamBoard.a"
)
set
(
KISSFFT_LIB
"libkissFFT.a"
)
#set(LOWLATENCY False)
#set(LOWLATENCY False)
elseif
(
${
RF_BOARD
}
STREQUAL
"ETHERNET"
)
elseif
(
${
RF_BOARD
}
STREQUAL
"ETHERNET"
)
...
@@ -1449,7 +1454,7 @@ add_executable(lte-softmodem
...
@@ -1449,7 +1454,7 @@ add_executable(lte-softmodem
target_link_libraries
(
lte-softmodem
target_link_libraries
(
lte-softmodem
-Wl,--start-group
-Wl,--start-group
RRC_LIB S1AP_LIB S1AP_ENB GTPV1U SECU_CN SECU_OSA UTIL HASHTABLE SCTP_CLIENT UDP SCHED_LIB PHY LFDS L2
${
MSC_LIB
}
${
RAL_LIB
}
${
NAS_UE_LIB
}
${
ITTI_LIB
}
${
MIH_LIB
}
${
LMS7002_LIB
}
${
Si5351C_LIB
}
RRC_LIB S1AP_LIB S1AP_ENB GTPV1U SECU_CN SECU_OSA UTIL HASHTABLE SCTP_CLIENT UDP SCHED_LIB PHY LFDS L2
${
MSC_LIB
}
${
RAL_LIB
}
${
NAS_UE_LIB
}
${
ITTI_LIB
}
${
MIH_LIB
}
${
LMS7002_LIB
}
${
Si5351C_LIB
}
${
LMSStreamBoard_LIB
}
${
KISSFFT_LIB
}
-Wl,--end-group
)
-Wl,--end-group
)
...
...
targets/ARCH/SODERA/USERSPACE/LIB/sodera_lib.cpp
View file @
9e28189d
...
@@ -52,6 +52,8 @@
...
@@ -52,6 +52,8 @@
#include "lmsComms.h"
#include "lmsComms.h"
#include "LMS7002M.h"
#include "LMS7002M.h"
#include "Si5351C.h"
#include "Si5351C.h"
#include "LMS_StreamBoard.h"
#ifdef __SSE4_1__
#ifdef __SSE4_1__
# include <smmintrin.h>
# include <smmintrin.h>
#endif
#endif
...
@@ -72,24 +74,12 @@ typedef struct
...
@@ -72,24 +74,12 @@ typedef struct
// --------------------------------
// --------------------------------
// variables for SoDeRa configuration
// variables for SoDeRa configuration
// --------------------------------
// --------------------------------
/*
uhd::usrp::multi_usrp::sptr usrp;
//uhd::usrp::multi_usrp::sptr rx_usrp;
//create a send streamer and a receive streamer
uhd::tx_streamer::sptr tx_stream;
uhd::rx_streamer::sptr rx_stream;
uhd::tx_metadata_t tx_md;
uhd::rx_metadata_t rx_md;
uhd::time_spec_t tm_spec;
//setup variables and allocate buffer
uhd::async_metadata_t async_md;
*/
LMScomms
Port
;
LMScomms
Port
;
Si5351C
Si
;
Si5351C
Si
;
LMS7002M
lmsControl
;
LMS_StreamBoard
*
lmsStream
;
double
sample_rate
;
double
sample_rate
;
// time offset between transmiter timestamp and receiver timestamp;
// time offset between transmiter timestamp and receiver timestamp;
double
tdiff
;
double
tdiff
;
...
@@ -105,13 +95,13 @@ typedef struct
...
@@ -105,13 +95,13 @@ typedef struct
int64_t
rx_count
;
int64_t
rx_count
;
openair0_timestamp
rx_timestamp
;
openair0_timestamp
rx_timestamp
;
}
sodera_
state_
t
;
}
sodera_t
;
sodera_
state_
t
sodera_state
;
sodera_t
sodera_state
;
static
int
trx_sodera_start
(
openair0_device
*
device
)
static
int
trx_sodera_start
(
openair0_device
*
device
)
{
{
sodera_
state_t
*
s
=
(
sodera_state
_t
*
)
device
->
priv
;
sodera_
t
*
s
=
(
sodera
_t
*
)
device
->
priv
;
// init recv and send streaming
// init recv and send streaming
...
@@ -124,7 +114,7 @@ static int trx_sodera_start(openair0_device *device)
...
@@ -124,7 +114,7 @@ static int trx_sodera_start(openair0_device *device)
static
void
trx_sodera_end
(
openair0_device
*
device
)
static
void
trx_sodera_end
(
openair0_device
*
device
)
{
{
sodera_
state_t
*
s
=
(
sodera_state
_t
*
)
device
->
priv
;
sodera_
t
*
s
=
(
sodera
_t
*
)
device
->
priv
;
...
@@ -132,7 +122,7 @@ static void trx_sodera_end(openair0_device *device)
...
@@ -132,7 +122,7 @@ static void trx_sodera_end(openair0_device *device)
static
int
trx_sodera_write
(
openair0_device
*
device
,
openair0_timestamp
timestamp
,
void
**
buff
,
int
nsamps
,
int
cc
,
int
flags
)
static
int
trx_sodera_write
(
openair0_device
*
device
,
openair0_timestamp
timestamp
,
void
**
buff
,
int
nsamps
,
int
cc
,
int
flags
)
{
{
sodera_
state_t
*
s
=
(
sodera_state
_t
*
)
device
->
priv
;
sodera_
t
*
s
=
(
sodera
_t
*
)
device
->
priv
;
if
(
cc
>
1
)
{
if
(
cc
>
1
)
{
// s->tx_stream->send(buff_ptrs, nsamps, s->tx_md);
// s->tx_stream->send(buff_ptrs, nsamps, s->tx_md);
...
@@ -145,7 +135,7 @@ static int trx_sodera_write(openair0_device *device, openair0_timestamp timestam
...
@@ -145,7 +135,7 @@ static int trx_sodera_write(openair0_device *device, openair0_timestamp timestam
static
int
trx_sodera_read
(
openair0_device
*
device
,
openair0_timestamp
*
ptimestamp
,
void
**
buff
,
int
nsamps
,
int
cc
)
static
int
trx_sodera_read
(
openair0_device
*
device
,
openair0_timestamp
*
ptimestamp
,
void
**
buff
,
int
nsamps
,
int
cc
)
{
{
sodera_
state_t
*
s
=
(
sodera_state
_t
*
)
device
->
priv
;
sodera_
t
*
s
=
(
sodera
_t
*
)
device
->
priv
;
int
samples_received
=
0
,
i
,
j
;
int
samples_received
=
0
,
i
,
j
;
int
nsamps2
;
// aligned to upper 32 or 16 byte boundary
int
nsamps2
;
// aligned to upper 32 or 16 byte boundary
#if defined(__x86_64) || defined(__i386__)
#if defined(__x86_64) || defined(__i386__)
...
@@ -193,7 +183,7 @@ static bool is_equal(double a, double b)
...
@@ -193,7 +183,7 @@ static bool is_equal(double a, double b)
int
trx_sodera_set_freq
(
openair0_device
*
device
,
openair0_config_t
*
openair0_cfg
,
int
dummy
)
{
int
trx_sodera_set_freq
(
openair0_device
*
device
,
openair0_config_t
*
openair0_cfg
,
int
dummy
)
{
sodera_
state_t
*
s
=
(
sodera_state
_t
*
)
device
->
priv
;
sodera_
t
*
s
=
(
sodera
_t
*
)
device
->
priv
;
// s->usrp->set_tx_freq(openair0_cfg[0].tx_freq[0]);
// s->usrp->set_tx_freq(openair0_cfg[0].tx_freq[0]);
// s->usrp->set_rx_freq(openair0_cfg[0].rx_freq[0]);
// s->usrp->set_rx_freq(openair0_cfg[0].rx_freq[0]);
...
@@ -204,7 +194,7 @@ int trx_sodera_set_freq(openair0_device* device, openair0_config_t *openair0_cfg
...
@@ -204,7 +194,7 @@ int trx_sodera_set_freq(openair0_device* device, openair0_config_t *openair0_cfg
int
openair0_set_rx_frequencies
(
openair0_device
*
device
,
openair0_config_t
*
openair0_cfg
)
{
int
openair0_set_rx_frequencies
(
openair0_device
*
device
,
openair0_config_t
*
openair0_cfg
)
{
sodera_
state_t
*
s
=
(
sodera_state
_t
*
)
device
->
priv
;
sodera_
t
*
s
=
(
sodera
_t
*
)
device
->
priv
;
static
int
first_call
=
1
;
static
int
first_call
=
1
;
static
double
rf_freq
,
diff
;
static
double
rf_freq
,
diff
;
...
@@ -222,7 +212,7 @@ int openair0_set_rx_frequencies(openair0_device* device, openair0_config_t *open
...
@@ -222,7 +212,7 @@ int openair0_set_rx_frequencies(openair0_device* device, openair0_config_t *open
int
trx_sodera_set_gains
(
openair0_device
*
device
,
int
trx_sodera_set_gains
(
openair0_device
*
device
,
openair0_config_t
*
openair0_cfg
)
{
openair0_config_t
*
openair0_cfg
)
{
sodera_
state_t
*
s
=
(
sodera_state
_t
*
)
device
->
priv
;
sodera_
t
*
s
=
(
sodera
_t
*
)
device
->
priv
;
// s->usrp->set_tx_gain(openair0_cfg[0].tx_gain[0]);
// s->usrp->set_tx_gain(openair0_cfg[0].tx_gain[0]);
// ::uhd::gain_range_t gain_range = s->usrp->get_rx_gain_range(0);
// ::uhd::gain_range_t gain_range = s->usrp->get_rx_gain_range(0);
...
@@ -313,7 +303,7 @@ int trx_sodera_reset_stats(openair0_device* device) {
...
@@ -313,7 +303,7 @@ int trx_sodera_reset_stats(openair0_device* device) {
int
openair0_dev_init_sodera
(
openair0_device
*
device
,
openair0_config_t
*
openair0_cfg
)
int
openair0_dev_init_sodera
(
openair0_device
*
device
,
openair0_config_t
*
openair0_cfg
)
{
{
sodera_
state_
t
*
s
=&
sodera_state
;
sodera_t
*
s
=&
sodera_state
;
size_t
i
;
size_t
i
;
...
@@ -332,7 +322,6 @@ int openair0_dev_init_sodera(openair0_device* device, openair0_config_t *openair
...
@@ -332,7 +322,6 @@ int openair0_dev_init_sodera(openair0_device* device, openair0_config_t *openair
(
int
)
devInfo
.
hardware
,
(
int
)
devInfo
.
hardware
,
(
int
)
devInfo
.
firmware
,
(
int
)
devInfo
.
firmware
,
(
int
)
devInfo
.
protocol
);
(
int
)
devInfo
.
protocol
);
LMS7002M
lmsControl
(
&
s
->
Port
);
printf
(
"Configuring Si5351C
\n
"
);
printf
(
"Configuring Si5351C
\n
"
);
s
->
Si
.
Initialize
(
&
s
->
Port
);
s
->
Si
.
Initialize
(
&
s
->
Port
);
...
@@ -396,29 +385,32 @@ int openair0_dev_init_sodera(openair0_device* device, openair0_config_t *openair
...
@@ -396,29 +385,32 @@ int openair0_dev_init_sodera(openair0_device* device, openair0_config_t *openair
break
;
break
;
}
}
s
->
lmsControl
=
LMS7002M
(
&
s
->
Port
);
liblms7_status
opStatus
;
liblms7_status
opStatus
;
lmsControl
.
ResetChip
();
s
->
lmsControl
.
ResetChip
();
opStatus
=
lmsControl
.
LoadConfig
(
openair0_cfg
[
0
].
configFilename
);
opStatus
=
s
->
lmsControl
.
LoadConfig
(
openair0_cfg
[
0
].
configFilename
);
if
(
opStatus
!=
LIBLMS7_SUCCESS
)
{
if
(
opStatus
!=
LIBLMS7_SUCCESS
)
{
printf
(
"Failed to load configuration file %s
\n
"
,
openair0_cfg
[
0
].
configFilename
);
printf
(
"Failed to load configuration file %s
\n
"
,
openair0_cfg
[
0
].
configFilename
);
exit
(
-
1
);
exit
(
-
1
);
}
}
opStatus
=
lmsControl
.
UploadAll
();
opStatus
=
s
->
lmsControl
.
UploadAll
();
if
(
opStatus
!=
LIBLMS7_SUCCESS
)
{
if
(
opStatus
!=
LIBLMS7_SUCCESS
)
{
printf
(
"Failed to upload configuration file
\n
"
);
printf
(
"Failed to upload configuration file
\n
"
);
exit
(
-
1
);
exit
(
-
1
);
}
}
opStatus
=
lmsControl
.
SetFrequencySX
(
LMS7002M
::
Tx
,
openair0_cfg
[
0
].
tx_freq
[
0
]
/
1e6
,
30.72
);
opStatus
=
s
->
lmsControl
.
SetFrequencySX
(
LMS7002M
::
Tx
,
openair0_cfg
[
0
].
tx_freq
[
0
]
/
1e6
,
30.72
);
if
(
opStatus
!=
LIBLMS7_SUCCESS
)
{
if
(
opStatus
!=
LIBLMS7_SUCCESS
)
{
printf
(
"Cannot set TX frequency %f MHz
\n
"
,
openair0_cfg
[
0
].
tx_freq
[
0
]
/
1e6
);
printf
(
"Cannot set TX frequency %f MHz
\n
"
,
openair0_cfg
[
0
].
tx_freq
[
0
]
/
1e6
);
exit
(
-
1
);
exit
(
-
1
);
}
}
opStatus
=
lmsControl
.
SetFrequencySX
(
LMS7002M
::
Rx
,
openair0_cfg
[
0
].
rx_freq
[
0
]
/
1e6
,
30.72
);
opStatus
=
s
->
lmsControl
.
SetFrequencySX
(
LMS7002M
::
Rx
,
openair0_cfg
[
0
].
rx_freq
[
0
]
/
1e6
,
30.72
);
if
(
opStatus
!=
LIBLMS7_SUCCESS
)
{
if
(
opStatus
!=
LIBLMS7_SUCCESS
)
{
printf
(
"Cannot set RX frequency %f MHz
\n
"
,
openair0_cfg
[
0
].
rx_freq
[
0
]
/
1e6
);
printf
(
"Cannot set RX frequency %f MHz
\n
"
,
openair0_cfg
[
0
].
rx_freq
[
0
]
/
1e6
);
...
@@ -428,12 +420,12 @@ int openair0_dev_init_sodera(openair0_device* device, openair0_config_t *openair
...
@@ -428,12 +420,12 @@ int openair0_dev_init_sodera(openair0_device* device, openair0_config_t *openair
// this makes RX/TX sampling rates equal
// this makes RX/TX sampling rates equal
opStatus
=
lmsControl
.
Modify_SPI_Reg_bits
(
EN_ADCCLKH_CLKGN
,
0
);
opStatus
=
s
->
lmsControl
.
Modify_SPI_Reg_bits
(
EN_ADCCLKH_CLKGN
,
0
);
if
(
opStatus
!=
LIBLMS7_SUCCESS
)
{
if
(
opStatus
!=
LIBLMS7_SUCCESS
)
{
printf
(
"Cannot modify SPI (EN_ADCCLKH_CLKGN)
\n
"
);
printf
(
"Cannot modify SPI (EN_ADCCLKH_CLKGN)
\n
"
);
exit
(
-
1
);
exit
(
-
1
);
}
}
opStatus
=
lmsControl
.
Modify_SPI_Reg_bits
(
CLKH_OV_CLKL_CGEN
,
2
);
opStatus
=
s
->
lmsControl
.
Modify_SPI_Reg_bits
(
CLKH_OV_CLKL_CGEN
,
2
);
if
(
opStatus
!=
LIBLMS7_SUCCESS
)
{
if
(
opStatus
!=
LIBLMS7_SUCCESS
)
{
printf
(
"Cannot modify SPI (CLKH_OV_CLKL_CGEN)
\n
"
);
printf
(
"Cannot modify SPI (CLKH_OV_CLKL_CGEN)
\n
"
);
exit
(
-
1
);
exit
(
-
1
);
...
@@ -442,26 +434,34 @@ int openair0_dev_init_sodera(openair0_device* device, openair0_config_t *openair
...
@@ -442,26 +434,34 @@ int openair0_dev_init_sodera(openair0_device* device, openair0_config_t *openair
const
float
cgen_freq_MHz
=
245.76
;
const
float
cgen_freq_MHz
=
245.76
;
const
int
interpolation
=
0
;
// real interpolation = 2
const
int
interpolation
=
0
;
// real interpolation = 2
const
int
decimation
=
0
;
// real decimation = 2
const
int
decimation
=
0
;
// real decimation = 2
opStatus
=
lmsControl
.
SetInterfaceFrequency
(
cgen_freq_MHz
,
interpolation
,
decimation
);
opStatus
=
s
->
lmsControl
.
SetInterfaceFrequency
(
cgen_freq_MHz
,
interpolation
,
decimation
);
if
(
opStatus
!=
LIBLMS7_SUCCESS
)
{
if
(
opStatus
!=
LIBLMS7_SUCCESS
)
{
printf
(
"Cannot SetInterfaceFrequency (%f,%d,%d)
\n
"
,
cgen_freq_MHz
,
interpolation
,
decimation
);
printf
(
"Cannot SetInterfaceFrequency (%f,%d,%d)
\n
"
,
cgen_freq_MHz
,
interpolation
,
decimation
);
exit
(
-
1
);
exit
(
-
1
);
}
}
// Run calibration procedure
// Run calibration procedure
float
txrx_calibrationBandwidth_MHz
=
5
;
float
txrx_calibrationBandwidth_MHz
=
5
;
opStatus
=
lmsControl
.
CalibrateTx
(
txrx_calibrationBandwidth_MHz
);
opStatus
=
s
->
lmsControl
.
CalibrateTx
(
txrx_calibrationBandwidth_MHz
);
if
(
opStatus
!=
LIBLMS7_SUCCESS
){
if
(
opStatus
!=
LIBLMS7_SUCCESS
){
printf
(
"TX Calibration failed
\n
"
);
printf
(
"TX Calibration failed
\n
"
);
exit
(
-
1
);
exit
(
-
1
);
}
}
opStatus
=
lmsControl
.
CalibrateRx
(
txrx_calibrationBandwidth_MHz
);
opStatus
=
s
->
lmsControl
.
CalibrateRx
(
txrx_calibrationBandwidth_MHz
);
if
(
opStatus
!=
LIBLMS7_SUCCESS
){
if
(
opStatus
!=
LIBLMS7_SUCCESS
){
printf
(
"RX Calibration failed
\n
"
);
printf
(
"RX Calibration failed
\n
"
);
exit
(
-
1
);
exit
(
-
1
);
}
}
s
->
lmsStream
=
new
LMS_StreamBoard
(
&
s
->
Port
);
LMS_StreamBoard
::
Status
opStreamStatus
;
// this will configure that sampling rate at output of FPGA
// this will configure that sampling rate at output of FPGA
// LMS_StreamBoard::ConfigurePLL(&s->Port,openair0_cfg[0].sample_rate,openair0_cfg[0].sample_rate,90);
opStreamStatus
=
s
->
lmsStream
->
ConfigurePLL
(
&
s
->
Port
,
openair0_cfg
[
0
].
sample_rate
,
openair0_cfg
[
0
].
sample_rate
,
90
);
if
(
opStatus
!=
LIBLMS7_SUCCESS
){
printf
(
"Sample rate programming failed
\n
"
);
exit
(
-
1
);
}
/*
/*
::uhd::gain_range_t gain_range = s->usrp->get_rx_gain_range(i);
::uhd::gain_range_t gain_range = s->usrp->get_rx_gain_range(i);
// limit to maximum gain
// limit to maximum gain
...
...
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