Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
M
Mock Turtle
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
1
Issues
1
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
Mock Turtle
Commits
1ae74421
Commit
1ae74421
authored
Oct 22, 2014
by
Tomasz Wlostowski
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
include: commented MQ headers, removed fixed constants
parent
40b79b0e
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
139 additions
and
78 deletions
+139
-78
mqueue.h
list/include/hw/mqueue.h
+97
-14
list-lib.c
list/lib/list-lib.c
+12
-5
list-lib.h
list/lib/list-lib.h
+3
-1
wrn-lib.cpp
list/lib/wrn-lib.cpp
+27
-58
No files found.
list/include/hw/mqueue.h
View file @
1ae74421
...
...
@@ -10,39 +10,122 @@
/*.
* White Rabbit Node Core
*
* mqueue.h: MQ register definitions
* mqueue.h: MQ register definitions
(Host side)
*/
#ifndef __MQUEUE_H
#define __MQUEUE_H
// Nax number of supported incoming/outgoing slots
#define MAX_MQUEUE_SLOTS 16
// HMQ base address (wrs to the base addr of the WR Node Core)
#define BASE_HMQ 0x00000
// Global Control Regs
#define MQUEUE_GCR (0x0)
// Incoming slot base address
#define MQUEUE_IN(slot) (0x4000 + (slot) * 0x400)
// Outgoung slot base address
#define MQUEUE_OUT(slot) (0x8000 + (slot) * 0x400)
// Global Control Registers base address, relative to BASE_HMQ (SLOT_COUNT, SLOT_STATUS, interrupt control, etc).
// Common for all incoming/outgoing slots in the queue
#define MQUEUE_BASE_GCR (0x0)
// Incoming slot base address, relative to BASE_HMQ
#define MQUEUE_BASE_IN(slot) (0x4000 + (slot) * 0x400)
// Outgoung slot base address, relative to BASE_HMQ
#define MQUEUE_BASE_OUT(slot) (0x8000 + (slot) * 0x400)
// MQ slot registers, relative to the base address of each slot: MQUEUE_BASE_IN(slot_no) or MQUEUE_BASE_OUT(slot_no)
#define MQUEUE_SLOT_COMMAND 0
// Status register
#define MQUEUE_SLOT_STATUS 4
// Start of data block
#define MQUEUE_SLOT_DATA_START 8
// Layout of MQUEUE_SLOT_COMMAND register:
//
MQ commands
//
Claim: prepares a slot to send a message (w/o)
#define MQUEUE_CMD_CLAIM (1<<24)
// Purge: erases all messages from a slot (w/o)
#define MQUEUE_CMD_PURGE (1<<25)
// Ready: pushes the message to the queue. (w/o)
#define MQUEUE_CMD_READY (1<<26)
// Discard: removes last message from the queue, advancing to the next one (w/o)
#define MQUEUE_CMD_DISCARD (1<<27)
// MQ slot register offsets
#define MQUEUE_SLOT_COMMAND 0
#define MQUEUE_SLOT_STATUS 4
#define MQUEUE_SLOT_DATA_START 8
// Size of the message to be sent, in words (w/o). Must be written together with the
// READY command, e.g.:
// writel (MQUEUE_CMD_READY | 10, MQUEUE_SLOT_COMMAND);
#define MQUEUE_CMD_MSG_SIZE_MASK 0xff
#define MQUEUE_CMD_MSG_SIZE_SHIFT 0
// Layout of MQUEUE_SLOT_STATUS register:
// [0] Slot is full
#define MQUEUE_SLOT_STATUS_FULL (1<<0)
// [1] Slot is empty
#define MQUEUE_SLOT_STATUS_EMPTY (1<<1)
// MQ GCR register offsets
// [15:8] Number of occupied entries
#define MQUEUE_SLOT_STATUS_OCCUPIED_SHIFT 8
#define MQUEUE_SLOT_STATUS_OCCUPIED_MASK 0xff00
// [23:16] Number of transferred words in the message currently on top of the slot
#define MQUEUE_SLOT_STATUS_MSG_SIZE_SHIFT 16
#define MQUEUE_SLOT_STATUS_MSG_SIZE_MASK 0xff0000
// [31:28] log2(number of words in the slot).
#define MQUEUE_SLOT_STATUS_LOG2_WIDTH_SHIFT 28
#define MQUEUE_SLOT_STATUS_LOG2_WIDTH_MASK 0xf0000000
// [7:2] log2(number of entries in the slot).
#define MQUEUE_SLOT_STATUS_LOG2_ENTRIES_SHIFT 2
#define MQUEUE_SLOT_STATUS_LOG2_ENTRIES_MASK 0xfc
//
// MQ Global Control Registers.Adresses relative to MQUEUE_BASE_GCR:
//
#define MQUEUE_GCR_INCOMING_STATUS_MASK (0x0000ffff)
// Number of slots in this implementation of HMQ
#define MQUEUE_GCR_SLOT_COUNT 0
// EMPTY bits of all slots in a single register (for polling/IRQ status)
#define MQUEUE_GCR_SLOT_STATUS 4
// Interrupt mask (Outgoing: EMPTY, Incoming: not EMPTY)
#define MQUEUE_GCR_IRQ_MASK 8
// IRQ Coalescing register (reserved for future use)
#define MQUEUE_GCR_IRQ_COALESCE 12
#define MQUEUE_SLOT_STATUS_FULL (1<<0)
// Layout of SLOT_COUNT register
// [7:0] Number of Incoming slots
#define MQUEUE_GCR_SLOT_COUNT_N_IN_SHIFT 0
#define MQUEUE_GCR_SLOT_COUNT_N_IN_MASK 0xff
// [15:8] Number of Outgoing slots
#define MQUEUE_GCR_SLOT_COUNT_N_OUT_SHIFT 8
#define MQUEUE_GCR_SLOT_COUNT_N_OUT_MASK 0xff00
// Layout of SLOT_STATUS register
// [15:0] Outgoing slots status. Each bit indicates a NOT EMPTY status of the corresponding outgoing slot
#define MQUEUE_GCR_SLOT_STATUS_OUT_SHIFT 0
#define MQUEUE_GCR_SLOT_STATUS_OUT_MASK 0xffff
// [31:16] Incoming slots status. Each bit indicates an EMPTY status of the corresponding incoming slot
#define MQUEUE_GCR_SLOT_STATUS_IN_SHIFT 16
#define MQUEUE_GCR_SLOT_STATUS_IN_MASK 0xffff0000
// Layout of IRQ_MASK register
// [15:0] Outgoing slots status interrupt mask. Each bit enables generation of interrupt on NOT EMPTY status of the corresponding outgoing slot.
#define MQUEUE_GCR_IRQ_MASK_OUT_SHIFT 0
#define MQUEUE_GCR_IRQ_MASK_OUT_MASK 0xffff
// [31:16] Incoming slots status interrupt mask. Each bit enables generation of interrupt on EMPTY status of the corresponding incoming slot.
#define MQUEUE_GCR_IRQ_MASK_IN_SHIFT 16
#define MQUEUE_GCR_IRQ_MASK_IN_MASK 0xffff0000
// Layout of IRQ_COALSESCE register
// The register is left for future IRQ coalescing support (if ever needed)
#endif
list/lib/list-lib.c
View file @
1ae74421
...
...
@@ -347,7 +347,7 @@ int list_out_trig_remove ( struct list_node *dev, struct list_trigger_handle *ha
req
[
1
]
=
handle
->
ptr_cond
;
req
[
2
]
=
handle
->
ptr_trig
;
printf
(
"remove: %x %x %x
\n
"
,
handle
->
channel
,
handle
->
ptr_cond
,
handle
->
ptr_trig
);
//
printf("remove: %x %x %x\n", handle->channel, handle->ptr_cond, handle->ptr_trig);
int
n
=
rt_request
(
dev
,
1
,
ID_FD_CMD_CHAN_REMOVE_TRIGGER
,
req
,
3
,
rsp
);
...
...
@@ -387,7 +387,7 @@ int list_out_trig_get_all (struct list_node *dev, int output, struct list_output
uint32_t
state
=
rsp
[
8
];
printf
(
"%d %d %x
\n
"
,
bucket
,
index
,
state
);
//
printf("%d %d %x\n", bucket, index, state);
if
(
state
!=
HASH_ENT_EMPTY
&&
!
(
state
&
HASH_ENT_CONDITIONAL
))
{
struct
list_output_trigger_state
*
current
=
&
triggers
[
count
];
...
...
@@ -402,6 +402,8 @@ int list_out_trig_get_all (struct list_node *dev, int output, struct list_output
current
->
delay_trig
.
cycles
=
rsp
[
6
];
current
->
delay_trig
.
frac
=
rsp
[
7
];
current
->
is_conditional
=
0
;
current
->
worst_latency_us
=
(
rsp
[
17
]
+
124
)
/
125
;
if
(
rsp
[
9
])
// condition assigned?
{
current
->
is_conditional
=
1
;
...
...
@@ -442,18 +444,23 @@ int list_out_get_state ( struct list_node *dev, int output, struct list_output_s
int
n
=
rt_request
(
dev
,
1
,
ID_FD_CMD_CHAN_GET_STATE
,
req
,
1
,
rsp
);
if
(
n
==
2
8
&&
rsp
[
0
]
==
ID_REP_STATE
)
if
(
n
==
2
9
&&
rsp
[
0
]
==
ID_REP_STATE
)
{
state
->
output
=
output
;
state
->
executed_pulses
=
rsp
[
3
];
state
->
missed_pulses_late
=
rsp
[
4
];
state
->
missed_pulses_deadtime
=
rsp
[
5
];
state
->
missed_pulses_overflow
=
rsp
[
6
];
state
->
total_rx_packets
=
rsp
[
27
];
state
->
rx_packets
=
rsp
[
27
];
state
->
rx_loopback
=
rsp
[
28
];
unbag_ts
(
rsp
,
10
,
&
state
->
last_executed
.
ts
);
unbag_ts
(
rsp
,
13
,
&
state
->
last_enqueued
.
ts
);
unbag_ts
(
rsp
,
16
,
&
state
->
last_programmed
.
ts
);
printf
(
"Correct
\n
"
);
return
0
;
}
...
...
@@ -470,5 +477,5 @@ int list_out_get_state ( struct list_node *dev, int output, struct list_output_s
obuf[24] = st->dead_time;
obuf[25] = st->width_cycles;
obuf[26] = st->worst_latency;*/
return
-
1
;
}
list/lib/list-lib.h
View file @
1ae74421
...
...
@@ -40,6 +40,7 @@ struct list_output_trigger_state {
struct
list_timestamp
delay_trig
;
struct
list_timestamp
delay_cond
;
struct
list_trigger_handle
handle
;
int
worst_latency_us
;
};
struct
list_output_state
{
...
...
@@ -60,7 +61,8 @@ struct list_output_state {
uint32_t
dead_time
;
uint32_t
pulse_width
;
struct
list_timestamp
worst_rx_delay
;
uint32_t
total_rx_packets
;
uint32_t
rx_packets
;
uint32_t
rx_loopback
;
};
struct
list_node
;
...
...
list/lib/wrn-lib.cpp
View file @
1ae74421
...
...
@@ -19,7 +19,6 @@
#define MAX_MQUEUE_SLOTS 16
#define BASE_HMQ 0x00000
#define BASE_CPU_CSR 0x10000
static
int
fd_base
=
1
;
...
...
@@ -53,10 +52,6 @@ static void wrn_writel(struct wrn_dev *dev, uint32_t data, uint32_t addr)
return
dev
->
fmc
->
writel
(
dev
->
fmc
,
data
,
dev
->
base
+
addr
);
}
#define HMQ_OUT(x) (0x8000 + 0x400 *(x))
#define HMQ_IN(x) (0x4000 + 0x400 *(x))
#define HMQ_GCR 0
static
uint32_t
hmq_readl
(
struct
wrn_dev
*
dev
,
uint32_t
what
,
uint32_t
offset
)
{
return
wrn_readl
(
dev
,
BASE_HMQ
+
what
+
offset
);
...
...
@@ -72,10 +67,10 @@ static int init_hmq( struct wrn_dev *dev )
uint32_t
slot_count
,
slot_status
;
int
i
,
entries
,
width
;
slot_count
=
hmq_readl
(
dev
,
HMQ
_GCR
,
MQUEUE_GCR_SLOT_COUNT
);
slot_count
=
hmq_readl
(
dev
,
MQUEUE_BASE
_GCR
,
MQUEUE_GCR_SLOT_COUNT
);
int
n_in
=
slot_count
&
0xff
;
int
n_out
=
(
slot_count
>>
8
)
&
0xff
;
int
n_in
=
(
slot_count
&
MQUEUE_GCR_SLOT_COUNT_N_IN_MASK
)
>>
MQUEUE_GCR_SLOT_COUNT_N_IN_SHIFT
;
int
n_out
=
(
slot_count
&
MQUEUE_GCR_SLOT_COUNT_N_OUT_MASK
)
>>
MQUEUE_GCR_SLOT_COUNT_N_OUT_SHIFT
;
dev
->
hmq
.
n_in
=
n_in
;
dev
->
hmq
.
n_out
=
n_out
;
...
...
@@ -84,18 +79,18 @@ static int init_hmq( struct wrn_dev *dev )
for
(
i
=
0
;
i
<
n_out
;
i
++
)
{
slot_status
=
hmq_readl
(
dev
,
HMQ
_OUT
(
i
),
MQUEUE_SLOT_STATUS
);
width
=
1
<<
((
slot_status
>>
28
)
&
0xf
);
entries
=
1
<<
((
slot_status
>>
2
)
&
0x3f
);
hmq_writel
(
dev
,
HMQ
_OUT
(
i
),
MQUEUE_CMD_PURGE
,
MQUEUE_SLOT_COMMAND
);
slot_status
=
hmq_readl
(
dev
,
MQUEUE_BASE
_OUT
(
i
),
MQUEUE_SLOT_STATUS
);
width
=
1
<<
((
slot_status
&
MQUEUE_SLOT_STATUS_LOG2_WIDTH_MASK
)
>>
MQUEUE_SLOT_STATUS_LOG2_WIDTH_SHIFT
);
entries
=
1
<<
((
slot_status
&
MQUEUE_SLOT_STATUS_LOG2_ENTRIES_MASK
)
>>
MQUEUE_SLOT_STATUS_LOG2_ENTRIES_SHIFT
);
hmq_writel
(
dev
,
MQUEUE_BASE
_OUT
(
i
),
MQUEUE_CMD_PURGE
,
MQUEUE_SLOT_COMMAND
);
DBG
(
" - out%d: width=%d, entries=%d
\n
"
,
i
,
width
,
entries
);
}
for
(
i
=
0
;
i
<
n_in
;
i
++
)
{
slot_status
=
hmq_readl
(
dev
,
HMQ
_IN
(
i
),
MQUEUE_SLOT_STATUS
);
width
=
1
<<
((
slot_status
>>
28
)
&
0xf
);
entries
=
1
<<
((
slot_status
>>
2
)
&
0x3f
);
hmq_writel
(
dev
,
HMQ
_IN
(
i
),
MQUEUE_CMD_PURGE
,
MQUEUE_SLOT_COMMAND
);
slot_status
=
hmq_readl
(
dev
,
MQUEUE_BASE
_IN
(
i
),
MQUEUE_SLOT_STATUS
);
width
=
1
<<
((
slot_status
&
MQUEUE_SLOT_STATUS_LOG2_WIDTH_MASK
)
>>
MQUEUE_SLOT_STATUS_LOG2_WIDTH_SHIFT
);
entries
=
1
<<
((
slot_status
&
MQUEUE_SLOT_STATUS_LOG2_ENTRIES_MASK
)
>>
MQUEUE_SLOT_STATUS_LOG2_ENTRIES_SHIFT
);
hmq_writel
(
dev
,
MQUEUE_BASE
_IN
(
i
),
MQUEUE_CMD_PURGE
,
MQUEUE_SLOT_COMMAND
);
DBG
(
" - in%d: width=%d, entries=%d
\n
"
,
i
,
width
,
entries
);
}
return
0
;
...
...
@@ -156,20 +151,14 @@ int wrn_cpu_count( struct wrn_dev* dev)
int
wrn_cpu_stop
(
struct
wrn_dev
*
dev
,
uint32_t
mask
)
{
uint32_t
r
=
wrn_readl
(
dev
,
BASE_CPU_CSR
+
WRN_CPU_CSR_REG_RESET
);
//DBG("cpu_stop: r %x mask %x csrbase %x\n", r, mask, BASE_CPU_CSR +WRN_CPU_CSR_REG_RESET);
r
|=
mask
;
//DBG("cpu_stop: r %x \n", r);
wrn_writel
(
dev
,
r
,
BASE_CPU_CSR
+
WRN_CPU_CSR_REG_RESET
);
wrn_writel
(
dev
,
r
|
mask
,
BASE_CPU_CSR
+
WRN_CPU_CSR_REG_RESET
);
return
0
;
}
int
wrn_cpu_start
(
struct
wrn_dev
*
dev
,
uint32_t
mask
)
{
uint32_t
r
=
wrn_readl
(
dev
,
BASE_CPU_CSR
+
WRN_CPU_CSR_REG_RESET
);
//DBG("cpu_stop: r %x mask %x csrbase %x\n", r, mask, BASE_CPU_CSR +WRN_CPU_CSR_REG_RESET);
r
&=
~
mask
;
//DBG("cpu_start: r %x \n", r);
wrn_writel
(
dev
,
r
,
BASE_CPU_CSR
+
WRN_CPU_CSR_REG_RESET
);
wrn_writel
(
dev
,
r
&
~
mask
,
BASE_CPU_CSR
+
WRN_CPU_CSR_REG_RESET
);
return
0
;
}
...
...
@@ -261,40 +250,31 @@ int wrn_cpu_load_file ( struct wrn_dev *dev, int cpu, const char *filename)
}
#define SLOT_OUT_MASK 0xffff
//
#define SLOT_OUT_MASK 0xffff
static
bool
do_rx
(
struct
wrn_dev
*
dev
,
wrn_message
&
msg
,
int
&
slot
)
{
uint32_t
in_stat
=
hmq_readl
(
dev
,
HMQ_GCR
,
MQUEUE_GCR_SLOT_STATUS
);
//uint32_t cnt = hmq_readl(dev, HMQ_GCR, MQUEUE_GCR_SLOT_COUNT);
uint32_t
in_stat
=
hmq_readl
(
dev
,
MQUEUE_BASE_GCR
,
MQUEUE_GCR_SLOT_STATUS
);
/*DBG("gcr %x\n", in_stat);
if(in_stat & 1)
{
DBG("Got!\n");
}*/
if
(
in_stat
&
SLOT_OUT_MASK
)
if
(
in_stat
&
MQUEUE_GCR_SLOT_STATUS_OUT_MASK
)
{
int
i
,
j
;
for
(
i
=
0
;
i
<
dev
->
hmq
.
n_out
;
i
++
)
{
if
(
in_stat
&
(
1
<<
i
))
{
uint32_t
slot_stat
=
hmq_readl
(
dev
,
HMQ
_OUT
(
i
),
MQUEUE_SLOT_STATUS
);
int
size
=
(
slot_stat
>>
16
)
&
0xff
;
uint32_t
slot_stat
=
hmq_readl
(
dev
,
MQUEUE_BASE
_OUT
(
i
),
MQUEUE_SLOT_STATUS
);
int
size
=
(
slot_stat
&
MQUEUE_SLOT_STATUS_MSG_SIZE_MASK
)
>>
MQUEUE_SLOT_STATUS_MSG_SIZE_SHIFT
;
msg
.
clear
();
for
(
j
=
0
;
j
<
size
;
j
++
)
for
(
j
=
0
;
j
<
size
;
j
++
)
{
uint32_t
rv
=
hmq_readl
(
dev
,
HMQ
_OUT
(
i
),
MQUEUE_SLOT_DATA_START
+
j
*
4
);
uint32_t
rv
=
hmq_readl
(
dev
,
MQUEUE_BASE
_OUT
(
i
),
MQUEUE_SLOT_DATA_START
+
j
*
4
);
msg
.
push_back
(
rv
);
//DBG("rv %x %x %c\n", MQUEUE_SLOT_DATA_START + j * 4, rv, (char) rv );
}
slot
=
i
;
hmq_writel
(
dev
,
HMQ_OUT
(
i
),
MQUEUE_CMD_DISCARD
,
MQUEUE_SLOT_COMMAND
);
slot
=
i
;
hmq_writel
(
dev
,
MQUEUE_BASE_OUT
(
i
),
MQUEUE_CMD_DISCARD
,
MQUEUE_SLOT_COMMAND
);
if
(
msg
[
0
]
==
0xdeadbeef
)
{
...
...
@@ -313,27 +293,22 @@ static bool do_rx(struct wrn_dev *dev, wrn_message& msg, int& slot)
return
false
;
}
//struct wrn_dev* wrn_open_by_fmc ( int device_id );
//struct wrn_dev* wrn_open( const char *device );
bool
tx_ready
(
struct
wrn_dev
*
dev
,
int
slot
)
{
uint32_t
slot_stat
=
hmq_readl
(
dev
,
HMQ_IN
(
slot
),
MQUEUE_SLOT_STATUS
);
uint32_t
slot_stat
=
hmq_readl
(
dev
,
MQUEUE_BASE_IN
(
slot
),
MQUEUE_SLOT_STATUS
);
return
!
(
slot_stat
&
MQUEUE_SLOT_STATUS_FULL
);
}
void
do_tx
(
struct
wrn_dev
*
dev
,
const
wrn_message
&
msg
,
int
slot
)
{
// DBG("do_tx: slot %d size %d\n", slot, msg.size());
hmq_writel
(
dev
,
HMQ
_IN
(
slot
),
MQUEUE_CMD_CLAIM
,
MQUEUE_SLOT_COMMAND
);
hmq_writel
(
dev
,
MQUEUE_BASE
_IN
(
slot
),
MQUEUE_CMD_CLAIM
,
MQUEUE_SLOT_COMMAND
);
for
(
int
i
=
0
;
i
<
msg
.
size
();
i
++
)
{
// DBG("%d: %x\n", i, msg[i]);
hmq_writel
(
dev
,
HMQ_IN
(
slot
),
msg
[
i
],
MQUEUE_SLOT_DATA_START
+
i
*
4
);
hmq_writel
(
dev
,
MQUEUE_BASE_IN
(
slot
),
msg
[
i
],
MQUEUE_SLOT_DATA_START
+
i
*
4
);
}
hmq_writel
(
dev
,
HMQ
_IN
(
slot
),
MQUEUE_CMD_READY
,
MQUEUE_SLOT_COMMAND
);
hmq_writel
(
dev
,
MQUEUE_BASE
_IN
(
slot
),
MQUEUE_CMD_READY
,
MQUEUE_SLOT_COMMAND
);
}
...
...
@@ -344,22 +319,17 @@ int update_mqueues( struct wrn_dev * dev )
int
slot
;
int
rx_limit
=
5
;
int
got_sth
=
0
;
// outgoing path (from CPUs)
while
(
do_rx
(
dev
,
msg
,
slot
)
&&
rx_limit
)
{
// DBG("RxLim %d\n", rx_limit);
rx_limit
--
;
got_sth
=
1
;
//DBG("slot %d rx %d words\n", slot, msg.size());
for
(
wrn_connmap
::
iterator
i
=
dev
->
fds
.
begin
();
i
!=
dev
->
fds
.
end
();
++
i
)
{
wrn_buffer
*
buf
=
i
->
second
->
out
;
if
(
buf
&&
buf
->
slot
==
slot
)
{
// DBG("-> queue to fd %d\n", i->first);
buf
->
lock
(
true
);
//DBG("locked\n");
buf
->
process
(
msg
);
buf
->
unlock
();
...
...
@@ -377,7 +347,6 @@ int update_mqueues( struct wrn_dev * dev )
{
buf
->
lock
(
true
);
got_sth
=
1
;
// DBG("DoTX\n");
do_tx
(
dev
,
buf
->
pop
(),
buf
->
slot
);
buf
->
unlock
();
}
...
...
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