Commit 01b0a765 authored by Federico Vaga's avatar Federico Vaga

Merge branch 'feature/linkerscript' into develop

Each firmware is responsible to define the CPU memory layout on which
it will run
parents 8f6ecb24 aef0350d
/* This is a generic configuration that should work on
the SPEC/SVEC demos FPGA */
MEMORY
{
ram :
ORIGIN = 0x00000000,
LENGTH = 32768 - 2048
stack :
ORIGIN = 32768 - 2048,
LENGTH = 2048
smem :
ORIGIN = 0x40200000,
LENGTH = 65536
}
/* This is a generic configuration that should work on
the SPEC/SVEC demos FPGA */
MEMORY
{
ram :
ORIGIN = 0x00000000,
LENGTH = 32768 - 2048
stack :
ORIGIN = 32768 - 2048,
LENGTH = 2048
smem :
ORIGIN = 0x40200000,
LENGTH = 65536
}
/* This is a generic configuration that should work on
the SPEC/SVEC demos FPGA */
MEMORY
{
ram :
ORIGIN = 0x00000000,
LENGTH = 32768 - 2048
stack :
ORIGIN = 32768 - 2048,
LENGTH = 2048
smem :
ORIGIN = 0x40200000,
LENGTH = 65536
}
/* This is a generic configuration that should work on
the SPEC/SVEC demos FPGA */
MEMORY
{
ram :
ORIGIN = 0x00000000,
LENGTH = 32768 - 2048
stack :
ORIGIN = 32768 - 2048,
LENGTH = 2048
smem :
ORIGIN = 0x40200000,
LENGTH = 65536
}
/* This is a generic configuration that should work on
the SPEC/SVEC demos FPGA */
MEMORY
{
ram :
ORIGIN = 0x00000000,
LENGTH = 32768 - 2048
stack :
ORIGIN = 32768 - 2048,
LENGTH = 2048
smem :
ORIGIN = 0x40200000,
LENGTH = 65536
}
/* This is a generic configuration that should work on
the SPEC/SVEC demos FPGA */
MEMORY
{
ram :
ORIGIN = 0x00000000,
LENGTH = 32768 - 2048
stack :
ORIGIN = 32768 - 2048,
LENGTH = 2048
smem :
ORIGIN = 0x40200000,
LENGTH = 65536
}
/* This is a generic configuration that should work on
the SPEC/SVEC demos FPGA */
MEMORY
{
ram :
ORIGIN = 0x00000000,
LENGTH = 32768 - 2048
stack :
ORIGIN = 32768 - 2048,
LENGTH = 2048
smem :
ORIGIN = 0x40200000,
LENGTH = 65536
}
/* This is a generic configuration that should work on
the SPEC/SVEC demos FPGA */
MEMORY
{
ram :
ORIGIN = 0x00000000,
LENGTH = 32768 - 2048
stack :
ORIGIN = 32768 - 2048,
LENGTH = 2048
smem :
ORIGIN = 0x40200000,
LENGTH = 65536
}
-include Makefile.specific
DIRS += firmware
all clean cleanall modules install modules_install: $(DIRS)
clean: TARGET = clean
cleanall: TARGET = cleanall
modules: TARGET = modules
install: TARGET = install
modules_install: TARGET = modules_install
$(DIRS):
$(MAKE) -C $@ $(TARGET)
.PHONY: all clean cleanall modules install modules_install
.PHONY: $(DIRS)
-include Makefile.specific
DIRS := fw-01
DIRS += fw-02
DIRS += fw-03
all clean cleanall modules install modules_install: $(DIRS)
clean: TARGET = clean
cleanall: TARGET = cleanall
modules: TARGET = modules
install: TARGET = install
modules_install: TARGET = modules_install
DOT-CONFIGS = $(addsuffix /.config,$(DIRS))
$(DIRS): $(DOT-CONFIGS)
$(MAKE) -C $@ $(TARGET)
$(DOT-CONFIGS):
$(MAKE) -C $(@D) defconfig
.PHONY: all clean cleanall modules install modules_install
.PHONY: $(DIRS)
mainmenu "Profiling 1 demo configuration"
comment "Project specific configuration"
source "Kconfig.mt"
OBJS = fw-profile.o
OBJS += # add other object files that you need
OUTPUT = fw-profile
TRTL ?= ../../../../
TRTL_SW = $(TRTL)/software
include $(TRTL_SW)/firmware/Makefile
#
# Automatically generated file; DO NOT EDIT.
# Profiling 1 demo configuration
#
#
# Project specific configuration
#
#
# Mock Turtle configuration
#
CONFIG_FPGA_APPLICATION_ID=0
CONFIG_RT_APPLICATION_ID=0
CONFIG_CFLAGS_OPT="-O0"
CONFIG_CFLAGS_EXTRA="-ggdb"
#
# Mock Turtle framework configuration
#
# CONFIG_MOCKTURTLE_FRAMEWORK_ENABLE is not set
CONFIG_MOCKTURTLE_FRAMEWORK_ACTION_ENABLE=y
# CONFIG_MOCKTURTLE_FRAMEWORK_VARIABLE_ENABLE is not set
# CONFIG_MOCKTURTLE_FRAMEWORK_BUFFER_ENABLE is not set
CONFIG_MOCKTURTLE_FRAMEWORK_PING_ENABLE=y
CONFIG_MOCKTURTLE_FRAMEWORK_VERSION_ENABLE=y
#
# Mock Turtle library configuration
#
CONFIG_MOCKTURTLE_LIBRARY_PRINT_ENABLE=y
CONFIG_MOCKTURTLE_LIBRARY_PRINT_DEBUG_ENABLE=y
# CONFIG_MOCKTURTLE_LIBRARY_PRINT_ERROR_ENABLE is not set
# CONFIG_MOCKTURTLE_LIBRARY_PRINT_MESSAGE_ENABLE is not set
#include <mockturtle-rt-serial.h>
#include <mockturtle-rt-profile.h>
int main()
{
pr_debug("%s:%d\n\r", __func__, __LINE__);
trtl_tpu_profile_start(1);
pr_debug("%s:%d\n\r", __func__, __LINE__);
trtl_tpu_probe_trigger(2);
pr_debug("%s:%d\n\r", __func__, __LINE__);
trtl_tpu_probe_trigger(3);
pr_debug("%s:%d\n\r", __func__, __LINE__);
trtl_tpu_probe_trigger(4);
pr_debug("%s:%d\n\r", __func__, __LINE__);
trtl_tpu_probe_trigger(5);
pr_debug("%s:%d\n\r", __func__, __LINE__);
trtl_tpu_profile_stop(6);
pr_debug("%s:%d\n\r", __func__, __LINE__);
return 0;
}
mainmenu "alarm_clock demo configuration"
comment "Project specific configuration"
source "Kconfig.mt"
OBJS = fw-profile.o
OBJS += # add other object files that you need
OUTPUT = fw-profile
TRTL ?= ../../../../
TRTL_SW = $(TRTL)/software
include $(TRTL_SW)/firmware/Makefile
#
# Automatically generated file; DO NOT EDIT.
# alarm_clock demo configuration
#
#
# Project specific configuration
#
#
# Mock Turtle configuration
#
CONFIG_FPGA_APPLICATION_ID=0
CONFIG_RT_APPLICATION_ID=0
CONFIG_CFLAGS_OPT="-O0"
CONFIG_CFLAGS_EXTRA="-ggdb"
#
# Mock Turtle framework configuration
#
# CONFIG_MOCKTURTLE_FRAMEWORK_ENABLE is not set
CONFIG_MOCKTURTLE_FRAMEWORK_ACTION_ENABLE=y
# CONFIG_MOCKTURTLE_FRAMEWORK_VARIABLE_ENABLE is not set
# CONFIG_MOCKTURTLE_FRAMEWORK_BUFFER_ENABLE is not set
CONFIG_MOCKTURTLE_FRAMEWORK_PING_ENABLE=y
CONFIG_MOCKTURTLE_FRAMEWORK_VERSION_ENABLE=y
#
# Mock Turtle library configuration
#
CONFIG_MOCKTURTLE_LIBRARY_PRINT_ENABLE=y
CONFIG_MOCKTURTLE_LIBRARY_PRINT_DEBUG_ENABLE=y
# CONFIG_MOCKTURTLE_LIBRARY_PRINT_ERROR_ENABLE is not set
# CONFIG_MOCKTURTLE_LIBRARY_PRINT_MESSAGE_ENABLE is not set
#include <mockturtle-rt-serial.h>
#include <mockturtle-rt-profile.h>
int main()
{
int i;
pr_debug("%s:%d\n\r", __func__, __LINE__);
trtl_tpu_profile_start(1);
for (i = 0; i < 10; ++i) {
trtl_tpu_probe_trigger(2);
pr_debug("%s:%d\n\r", __func__, __LINE__);
trtl_tpu_probe_trigger(3);
}
trtl_tpu_profile_stop(4);
pr_debug("%s:%d\n\r", __func__, __LINE__);
return 0;
}
mainmenu "alarm_clock demo configuration"
comment "Project specific configuration"
source "Kconfig.mt"
OBJS = fw-profile.o
OBJS += # add other object files that you need
OUTPUT = fw-profile
TRTL ?= ../../../../
TRTL_SW = $(TRTL)/software
include $(TRTL_SW)/firmware/Makefile
#
# Automatically generated file; DO NOT EDIT.
# alarm_clock demo configuration
#
#
# Project specific configuration
#
#
# Mock Turtle configuration
#
CONFIG_FPGA_APPLICATION_ID=0
CONFIG_RT_APPLICATION_ID=0
CONFIG_CFLAGS_OPT="-O0"
CONFIG_CFLAGS_EXTRA="-ggdb"
#
# Mock Turtle framework configuration
#
# CONFIG_MOCKTURTLE_FRAMEWORK_ENABLE is not set
CONFIG_MOCKTURTLE_FRAMEWORK_ACTION_ENABLE=y
# CONFIG_MOCKTURTLE_FRAMEWORK_VARIABLE_ENABLE is not set
# CONFIG_MOCKTURTLE_FRAMEWORK_BUFFER_ENABLE is not set
CONFIG_MOCKTURTLE_FRAMEWORK_PING_ENABLE=y
CONFIG_MOCKTURTLE_FRAMEWORK_VERSION_ENABLE=y
#
# Mock Turtle library configuration
#
CONFIG_MOCKTURTLE_LIBRARY_PRINT_ENABLE=y
CONFIG_MOCKTURTLE_LIBRARY_PRINT_DEBUG_ENABLE=y
# CONFIG_MOCKTURTLE_LIBRARY_PRINT_ERROR_ENABLE is not set
# CONFIG_MOCKTURTLE_LIBRARY_PRINT_MESSAGE_ENABLE is not set
#include <mockturtle-rt-serial.h>
#include <mockturtle-rt-profile.h>
#define PROFILE_PROBE_ID0 0
#define PROFILE_PROBE_ID1 1
static int num = 0;
static void profile_short(void)
{
int i = 0;
pr_debug("%s:%d\n\r", __func__, __LINE__);
trtl_tpu_probe_trigger(3); /* start */
for (i = 0; i < 100; ++i)
num *= 2;
trtl_tpu_probe_trigger(4); /* end */
pr_debug("%s:%d\n\r", __func__, __LINE__);
}
static void profile_long(void)
{
int i = 0;
pr_debug("%s:%d\n\r", __func__, __LINE__);
trtl_tpu_probe_trigger(5); /* start */
for (i = 0; i < 10000; ++i) {
num /= 2;
num++;
}
trtl_tpu_probe_trigger(6); /* end */
pr_debug("%s:%d\n\r", __func__, __LINE__);
}
static void profile_func(void)
{
pr_debug("%s:%d\n\r", __func__, __LINE__);
trtl_tpu_profile_start(1);
profile_short();
profile_long();
trtl_tpu_profile_stop(2);
pr_debug("%s:%d\n\r", __func__, __LINE__);
}
int main()
{
pr_debug("%s:%d\n\r", __func__, __LINE__);
profile_func();
pr_debug("%s:%d\n\r", __func__, __LINE__);
return 0;
}
......@@ -56,7 +56,8 @@ you have to specify what to build, for example::
# Optional (prefer default when possible)
EXTRA_CFLAGS :=
MOCKTURTLE_LDSCRIPT := myfirmware.ld
TRTL_LD_SCRIPT := myfirmware.ld
TRTL_LD_DIR := path/to/linker-script/directory
Here the list of supported TBuild variables
......@@ -71,10 +72,38 @@ OUTPUT
EXTRA_CFLAGS
(Optional) Additional compiler options.
MOCKTURTLE_LDSCRIPT
TRTL_LD_SCRIPT
(Optional) Local path to the linker script.
The default is the standard Mock Turtle linker script.
TRTL_LD_DIR
(Optional) Path to the directory containing the ``trtl-memory.ld`` file.
By default this is set to the source's directory
Another requirement for a successful build is a linker script file containing
the ``MEMORY`` command. This linker script file must be named
``trtl-memory.ld`` and its content should look like this::
MEMORY
{
ram : ORIGIN = 0x00000000, LENGTH = 32768 - 2048
stack : ORIGIN = 32768 - 2048, LENGTH = 2048
smem : ORIGIN = 0x40200000, LENGTH = 65536
}
Unless you are modifying the Mock Turtle core itself, the following values are
fixed: ``ORIGIN = 0x00000000`` for the *ram*, ``ORIGIN = 0x40200000`` for
the *smem*. The ``LENGTH`` value for *ram* depends on the CPU memory size on
which the firmware will run; the ``LENGTH`` value for *smem* depends on the
Mock Turtle shared memory size; both these values depend on the FPGA synthesis
configuration of the target Mock Turtle instance.
.. note::
It is possible to add more linker script commands to ``trtl-memory.ld`` but
then the behavior is undefined. If you need more linker script commands,
please write your own linker script file and pass it to the build system
by using ``TRTL_LD_SCRIPT``.
You can build such firmware application by calling ``make`` from the
application directory (where the ``TBuild`` file is) like this::
......
Subproject commit 3899b6ae77e00dbbe216de94336ca1496c44b040
Subproject commit b57528861e0ee8351b87dc3d4ec4da6a118b4a48
......@@ -173,7 +173,6 @@ TIMESPEC TS_clk_125m_pllref_n_i = PERIOD "clk_125m_pllref_n_i" 8 ns HIGH 50%;
# GN4124
NET "gn_rst_n_i" TIG;
NET "cmp_gn4124_core/cmp_wrapped_gn4124/rst_*" TIG;
# Ignore async reset inputs to reset synchronisers
NET "*/gc_reset_async_in" TIG;
......
......@@ -37,6 +37,9 @@ CROSS_COMPILE_TARGET ?= riscv32-elf-
CFLAGS += -D__TRTL_FIRMWARE__
CFLAGS += -mabi=ilp32 -march=rv32im -ffunction-sections -fdata-sections
LDFLAGS += -lgcc -lc -Wl,--gc-sections
TRTL_LD_DIR ?= $(src)
LDFLAGS += -L$(TRTL_FW)/urv/
LDFLAGS += -L$(TRTL_LD_DIR)
# provide search patch for sources
vpath %.c $(TRTL_FW)
......@@ -98,7 +101,9 @@ OBJS_BUILD = $(addprefix $(BUILDDIR)/, $(OBJS))
OBJDIR_BUILD = $(addprefix $(BUILDDIR)/, $(OBJDIR))
OBJDIR_BUILD += $(BUILDDIR)
MOCKTURTLE_LDSCRIPT ?= $(TRTL_FW)/urv/mockturtle.ld
TRTL_LD_SCRIPT ?= $(TRTL_FW)/urv/mockturtle.ld
.PHONY: all clean cleanall
......@@ -127,9 +132,9 @@ $(OBJDIR_BUILD):
$(OBJS_BUILD): | $(AUTOCONF)
$(OBJS_BUILD): | $(OBJDIR_BUILD)
$(build_output).elf: $(addprefix $(TRTL_SW)/include/mockturtle/hw/,$(WBGEN_HEADERS)) $(MOCKTURTLE_LDSCRIPT) $(OBJS_BUILD)
$(build_output).elf: $(addprefix $(TRTL_SW)/include/mockturtle/hw/,$(WBGEN_HEADERS)) $(TRTL_LD_SCRIPT) $(OBJS_BUILD)
$(warning $(OBJS_BUILD))
${CC} $(CFLAGS) $(LDFLAGS) -o $(build_output).elf -nostartfiles $(OBJS_BUILD) -T $(MOCKTURTLE_LDSCRIPT)
${CC} $(CFLAGS) $(LDFLAGS) -o $(build_output).elf -nostartfiles $(OBJS_BUILD) -T $(TRTL_LD_SCRIPT)
$(build_output).bin: $(build_output).elf
${OBJCOPY} --remove-section .smem -O binary $(build_output).elf $(build_output).bin
......
OUTPUT_FORMAT("elf32-littleriscv")
ENTRY(_start)
MEMORY
{
ram :
ORIGIN = 0x00000000,
LENGTH = 32768 - 2048
stack :
ORIGIN = 32768 - 2048,
LENGTH = 2048
smem :
ORIGIN = 0x40200000,
LENGTH = 65536
}
SECTIONS
{
/*--------------------------------------------------------------------*/
/* Code and read-only segment */
/*--------------------------------------------------------------------*/
/* Begining of code and text segment */
. = 0x00000000;
_ftext = .;
PROVIDE( eprol = . );
/* text: Program code section */
.text :
{
*(.boot)
*(.text)
*(.text.*)
*(.gnu.linkonce.t.*)
} > ram
/* init: Code to execute before main (called by crt0.S) */
.init :
{
KEEP( *(.init) )
} > ram
/* fini: Code to execute after main (called by crt0.S) */
.fini :
{
KEEP( *(.fini) )
} > ram
/* rodata: Read-only data */
.rodata :
{
*(.rdata)
*(.rodata)
*(.rodata.*)
*(.gnu.linkonce.r.*)
} > ram
/* End of code and read-only segment */
PROVIDE( etext = . );
_etext = .;
/*--------------------------------------------------------------------*/
/* Global constructor/destructor segement */
/*--------------------------------------------------------------------*/
/* The .ctors/.dtors sections are special sections which contain a
list of constructor/destructor function pointers. crtbegin.o
includes code in a .init section which goes through the .ctors list
and calls each constuctor. crtend.o includes code in a .fini
section which goes through the .dtors list and calls each
destructor. crtbegin.o includes a special null pointer in its own
.ctors/.dtors sections which acts as a start indicator for those
lists. crtend.o also includes a special null pointer in its own
.ctors/.dtors sections which acts as an end indictor. The linker
commands below are setup so that crtbegin.o's .ctors/.dtors
sections are always first and crtend.o's .ctors/.dtors sections are
always last. This is the only way the list of functions will have
the begin and end indicators in the right place. */
/* ctors : Array of global constructor function pointers */
/*--------------------------------------------------------------------*/
/* Initialized data segment */
/*--------------------------------------------------------------------*/
/* Start of initialized data segment */
. = ALIGN(16);
_fdata = .;
/* data: Writable data */
.data :
{
*(.data)
*(.data.*)
*(.gnu.linkonce.d.*)
} > ram
/* End of initialized data segment */
PROVIDE( edata = . );
_edata = .;
/* Have _gp point to middle of sdata/sbss to maximize displacement range */
. = ALIGN(16);
_gp = . + 0x800;
/* Writable small data segment */
.sdata :
{
*(.sdata)
*(.sdata.*)
*(.srodata.*)
*(.gnu.linkonce.s.*)
} > ram
/*--------------------------------------------------------------------*/
/* Uninitialized data segment */
/*--------------------------------------------------------------------*/
/* Start of uninitialized data segment */
. = ALIGN(8);
_fbss = .;
/* Writable uninitialized small data segment */
.sbss :
{
*(.sbss)
*(.sbss.*)
*(.gnu.linkonce.sb.*)
} > ram
/* bss: Uninitialized writeable data section */
. = .;
_bss_start = .;
.bss :
{
*(.bss)
*(.bss.*)
*(.gnu.linkonce.b.*)
*(COMMON)
} > ram
/* End of uninitialized data segment (used by syscalls.c for heap) */
PROVIDE( end = . );
_end = ALIGN(8);
.smem : { *(.smem) } > smem
PROVIDE(_endram = ORIGIN(stack));
PROVIDE(_fexception_stack = ORIGIN(stack) + LENGTH(stack) - 4);
PROVIDE(_fstack = ORIGIN(stack) + LENGTH(stack) - 4 - 0x400 );
}
INCLUDE trtl-memory.ld
INCLUDE trtl-sections.ld
SECTIONS
{
/*--------------------------------------------------------------------*/
/* Code and read-only segment */
/*--------------------------------------------------------------------*/
/* Begining of code and text segment */
. = 0x00000000;
_ftext = .;
PROVIDE( eprol = . );
/* text: Program code section */
.text :
{
*(.boot)
*(.text)
*(.text.*)
*(.gnu.linkonce.t.*)
} > ram
/* init: Code to execute before main (called by crt0.S) */
.init :
{
KEEP( *(.init) )
} > ram
/* fini: Code to execute after main (called by crt0.S) */
.fini :
{
KEEP( *(.fini) )
} > ram
/* rodata: Read-only data */
.rodata :
{
*(.rdata)
*(.rodata)
*(.rodata.*)
*(.gnu.linkonce.r.*)
} > ram
/* End of code and read-only segment */
PROVIDE( etext = . );
_etext = .;
/*--------------------------------------------------------------------*/
/* Global constructor/destructor segement */
/*--------------------------------------------------------------------*/
/* The .ctors/.dtors sections are special sections which contain a
list of constructor/destructor function pointers. crtbegin.o
includes code in a .init section which goes through the .ctors list
and calls each constuctor. crtend.o includes code in a .fini
section which goes through the .dtors list and calls each
destructor. crtbegin.o includes a special null pointer in its own
.ctors/.dtors sections which acts as a start indicator for those
lists. crtend.o also includes a special null pointer in its own
.ctors/.dtors sections which acts as an end indictor. The linker
commands below are setup so that crtbegin.o's .ctors/.dtors
sections are always first and crtend.o's .ctors/.dtors sections are
always last. This is the only way the list of functions will have
the begin and end indicators in the right place. */
/* ctors : Array of global constructor function pointers */
/*--------------------------------------------------------------------*/
/* Initialized data segment */
/*--------------------------------------------------------------------*/
/* Start of initialized data segment */
. = ALIGN(16);
_fdata = .;
/* data: Writable data */
.data :
{
*(.data)
*(.data.*)
*(.gnu.linkonce.d.*)
} > ram
/* End of initialized data segment */
PROVIDE( edata = . );
_edata = .;
/* Have _gp point to middle of sdata/sbss to maximize displacement range */
. = ALIGN(16);
_gp = . + 0x800;
/* Writable small data segment */
.sdata :
{
*(.sdata)
*(.sdata.*)
*(.srodata.*)
*(.gnu.linkonce.s.*)
} > ram
/*--------------------------------------------------------------------*/
/* Uninitialized data segment */
/*--------------------------------------------------------------------*/
/* Start of uninitialized data segment */
. = ALIGN(8);
_fbss = .;
/* Writable uninitialized small data segment */
.sbss :
{
*(.sbss)
*(.sbss.*)
*(.gnu.linkonce.sb.*)
} > ram
/* bss: Uninitialized writeable data section */
. = .;
_bss_start = .;
.bss :
{
*(.bss)
*(.bss.*)
*(.gnu.linkonce.b.*)
*(COMMON)
} > ram
/* End of uninitialized data segment (used by syscalls.c for heap) */
PROVIDE( end = . );
_end = ALIGN(8);
.smem : { *(.smem) } > smem
PROVIDE(_endram = ORIGIN(stack));
PROVIDE(_fexception_stack = ORIGIN(stack) + LENGTH(stack) - 4);
PROVIDE(_fstack = ORIGIN(stack) + LENGTH(stack) - 4 - 0x400 );
}
../trtl-memory.ld
\ No newline at end of file
../trtl-memory.ld
\ No newline at end of file
../trtl-memory.ld
\ No newline at end of file
../trtl-memory.ld
\ No newline at end of file
../trtl-memory.ld
\ No newline at end of file
../trtl-memory.ld
\ No newline at end of file
../trtl-memory.ld
\ No newline at end of file
../trtl-memory.ld
\ No newline at end of file
../trtl-memory.ld
\ No newline at end of file
../trtl-memory.ld
\ No newline at end of file
../trtl-memory.ld
\ No newline at end of file
../trtl-memory.ld
\ No newline at end of file
/* This is a generic configuration that should work on
the SPEC/SVEC demos FPGA */
MEMORY
{
ram :
ORIGIN = 0x00000000,
LENGTH = 32768 - 2048
stack :
ORIGIN = 32768 - 2048,
LENGTH = 2048
smem :
ORIGIN = 0x40200000,
LENGTH = 65536
}
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