From 1e08789e9168ae0801ac5d4caee73f17f4ab7d74 Mon Sep 17 00:00:00 2001 From: Chris Sidebottom Date: Thu, 26 Aug 2021 10:43:46 +0100 Subject: [PATCH] Remove unused test runner files --- .../test_ethosu/fvp_test_runner/Makefile | 113 ------- .../fvp_test_runner/ethosu_test_runner.c | 152 --------- .../test_ethosu/fvp_test_runner/platform.ld | 302 ------------------ .../test_ethosu/fvp_test_runner/run_fvp.sh | 30 -- 4 files changed, 597 deletions(-) delete mode 100644 tests/python/contrib/test_ethosu/fvp_test_runner/Makefile delete mode 100644 tests/python/contrib/test_ethosu/fvp_test_runner/ethosu_test_runner.c delete mode 100644 tests/python/contrib/test_ethosu/fvp_test_runner/platform.ld delete mode 100755 tests/python/contrib/test_ethosu/fvp_test_runner/run_fvp.sh diff --git a/tests/python/contrib/test_ethosu/fvp_test_runner/Makefile b/tests/python/contrib/test_ethosu/fvp_test_runner/Makefile deleted file mode 100644 index 450a1bedfc13e..0000000000000 --- a/tests/python/contrib/test_ethosu/fvp_test_runner/Makefile +++ /dev/null @@ -1,113 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -# Makefile to build ethosu_test_runner - -# Setup build environment -build_dir := build -TVM_ROOT=$(shell cd ../../../../..; pwd) -CRT_ROOT ?= ${TVM_ROOT}/build/standalone_crt -ifeq ($(shell ls -lhd $(CRT_ROOT)),) -$(error "CRT not found. Ensure you have built the standalone_crt target and try again") -endif - -ARM_CPU=ARMCM55 -DMLC_CORE=${TVM_ROOT}/3rdparty/dmlc-core -ETHOSU_PATH=/opt/arm/ethosu -CMSIS_PATH=${ETHOSU_PATH}/cmsis -PLATFORM_PATH=${ETHOSU_PATH}/core_platform/targets/corstone-300 -PKG_COMPILE_OPTS = -g -Wall -O2 -Wno-incompatible-pointer-types -Wno-format -mcpu=cortex-m55 -mthumb -mfloat-abi=hard -std=gnu99 -CC = arm-none-eabi-gcc -AR = arm-none-eabi-ar -RANLIB = arm-none-eabi-ranlib -CC_OPTS = CC=$(CC) AR=$(AR) RANLIB=$(RANLIB) -PKG_CFLAGS = ${PKG_COMPILE_OPTS} \ - -I${TVM_ROOT}/include \ - -I${TVM_ROOT}/src/runtime/crt/include \ - -I${TVM_ROOT}/src/runtime/contrib \ - -I${DMLC_CORE}/include \ - -I${TVM_ROOT}/3rdparty/dlpack/include \ - -Icrt_config \ - -I${PLATFORM_PATH} \ - -I${ETHOSU_PATH}/core_driver/include \ - -I${CMSIS_PATH}/Device/ARM/${ARM_CPU}/Include/ \ - -I${CMSIS_PATH}/CMSIS/Core/Include \ - -I$(abspath $(build_dir))/codegen/host/include \ - -DETHOSU_TEST_RUNNER_TOL=${ETHOSU_TEST_RUNNER_TOL} \ - -DETHOSU_TEST_RUNNER_INPUT=${ETHOSU_TEST_RUNNER_INPUT} - -PKG_LDFLAGS = -lm -specs=nosys.specs -static -T platform.ld - -$(ifeq VERBOSE,1) -QUIET ?= -$(else) -QUIET ?= @ -$(endif) - -CRT_SRCS = $(shell find $(CRT_ROOT)) -CODEGEN_SRCS = $(shell find $(abspath $(build_dir))/codegen/host/src/*.c) -CODEGEN_OBJS = $(subst .c,.o,$(CODEGEN_SRCS)) -CMSIS_STARTUP_SRCS = $(shell find ${CMSIS_PATH}/Device/ARM/${ARM_CPU}/Source/*.c) -UART_SRCS = $(shell find ${PLATFORM_PATH}/*.c) - -ethosu_test_runner: $(build_dir)/ethosu_test_runner - -$(build_dir)/aot_executor.o: $(TVM_ROOT)/src/runtime/crt/aot_executor/aot_executor.c - $(QUIET)mkdir -p $(@D) - $(QUIET)$(CC) -c $(PKG_CFLAGS) -o $@ $^ - -$(build_dir)/stack_allocator.o: $(TVM_ROOT)/src/runtime/crt/memory/stack_allocator.c - $(QUIET)mkdir -p $(@D) - $(QUIET)$(CC) -c $(PKG_CFLAGS) -o $@ $^ - -$(build_dir)/crt_backend_api.o: $(TVM_ROOT)/src/runtime/crt/common/crt_backend_api.c - $(QUIET)mkdir -p $(@D) - $(QUIET)$(CC) -c $(PKG_CFLAGS) -o $@ $^ - -$(build_dir)/libcodegen.a: $(CODEGEN_SRCS) - $(QUIET)cd $(abspath $(build_dir)/codegen/host/src) && $(CC) -c $(PKG_CFLAGS) $(CODEGEN_SRCS) - $(QUIET)$(AR) -cr $(abspath $(build_dir)/libcodegen.a) $(CODEGEN_OBJS) - $(QUIET)$(RANLIB) $(abspath $(build_dir)/libcodegen.a) - -${build_dir}/libcmsis_startup.a: $(CMSIS_STARTUP_SRCS) - $(QUIET)mkdir -p $(abspath $(build_dir)/libcmsis_startup) - $(QUIET)cd $(abspath $(build_dir)/libcmsis_startup) && $(CC) -c $(PKG_CFLAGS) -D${ARM_CPU} $^ - $(QUIET)$(AR) -cr $(abspath $(build_dir)/libcmsis_startup.a) $(abspath $(build_dir))/libcmsis_startup/*.o - $(QUIET)$(RANLIB) $(abspath $(build_dir)/libcmsis_startup.a) - -${build_dir}/libuart.a: $(UART_SRCS) - $(QUIET)mkdir -p $(abspath $(build_dir)/libuart) - $(QUIET)cd $(abspath $(build_dir)/libuart) && $(CC) -c $(PKG_CFLAGS) $^ - $(QUIET)$(AR) -cr $(abspath $(build_dir)/libuart.a) $(abspath $(build_dir))/libuart/*.o - $(QUIET)$(RANLIB) $(abspath $(build_dir)/libuart.a) - -${build_dir}/ethosu_core_driver/libethosu_core_driver.a: - $(QUIET)cd $(abspath $(build_dir)/ethosu_core_driver) && $(MAKE) - -$(build_dir)/ethosu_test_runner: ethosu_test_runner.c $(build_dir)/aot_executor.o $(build_dir)/stack_allocator.o $(build_dir)/crt_backend_api.o ${build_dir}/libcodegen.a ${build_dir}/libcmsis_startup.a ${build_dir}/ethosu_core_driver/libethosu_core_driver.a ${build_dir}/libuart.a - $(QUIET)mkdir -p $(@D) - $(QUIET)$(CC) $(PKG_CFLAGS) -o $@ $^ $(PKG_LDFLAGS) - -clean: - $(QUIET)rm -rf $(build_dir)/crt - -cleanall: - $(QUIET)rm -rf $(build_dir) - -.SUFFIXES: - -.DEFAULT: ethosu_test_runner diff --git a/tests/python/contrib/test_ethosu/fvp_test_runner/ethosu_test_runner.c b/tests/python/contrib/test_ethosu/fvp_test_runner/ethosu_test_runner.c deleted file mode 100644 index 77db014238d21..0000000000000 --- a/tests/python/contrib/test_ethosu/fvp_test_runner/ethosu_test_runner.c +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ethosu_mod.h" -#include "expected_output_data.h" -#include "input_data.h" -#include "output_data.h" -#include "tvmgen_default.h" -#include "uart.h" - -#define WORKSPACE_SIZE (16384 * 1024) -__attribute__((section("crt_memory_sec"))) static uint8_t g_aot_memory[WORKSPACE_SIZE]; - -tvm_workspace_t app_workspace; - -void __attribute__((noreturn)) TVMPlatformAbort(tvm_crt_error_t error_code) { - printf("TVMPlatformAbort: %d\n", error_code); - printf("EXITTHESIM\n"); - exit(-1); -} - -tvm_crt_error_t TVMPlatformMemoryAllocate(size_t num_bytes, DLDevice dev, void** out_ptr) { - return StackMemoryManager_Allocate(&app_workspace, num_bytes, out_ptr); -} - -tvm_crt_error_t TVMPlatformMemoryFree(void* ptr, DLDevice dev) { - return StackMemoryManager_Free(&app_workspace, ptr); -} - -void TVMLogf(const char* msg, ...) { - va_list args; - va_start(args, msg); - vfprintf(stdout, msg, args); - va_end(args); -} - -TVM_DLL int TVMFuncRegisterGlobal(const char* name, TVMFunctionHandle f, int override) {} - -#define STRINGIZE(x) #x -#define STRINGIZE_VALUE_OF(x) STRINGIZE(x) - -#ifndef MAXDUMP -#define MAXDUMP 100 -#endif - -int abs(int v) { return v * ((v > 0) - (v < 0)); } - -struct ExcContext { - uint32_t r0; - uint32_t r1; - uint32_t r2; - uint32_t r3; - uint32_t r12; - uint32_t lr; - uint32_t pc; - uint32_t xPsr; -}; - -void HardFault_Handler() { - int irq; - struct ExcContext* e; - uint32_t sp; - - asm volatile( - "mrs %0, ipsr \n" // Read IPSR (Exception number) - "sub %0, #16 \n" // Get it into IRQn_Type range - "tst lr, #4 \n" // Select the stack which was in use - "ite eq \n" - "mrseq %1, msp \n" - "mrsne %1, psp \n" - "mov %2, sp \n" - : "=r"(irq), "=r"(e), "=r"(sp)); - - printf("Hard fault. irq=%d, pc=0x%08lu, lr=0x%08lu, xpsr=0x%08lu, sp=0x%08lu\n", irq, e->pc, - e->lr, e->xPsr, sp); - printf("%11s cfsr=0x%08lu bfar=0x%08lu\n", "", SCB->CFSR, SCB->BFAR); - printf("EXITTHESIM\n"); - while (1 == 1) - ; -} - -int main(int argc, char** argv) { - uart_init(); - printf("Starting Test\n"); - EthosuInit(); - int result = 0; - - printf("Allocating memory\n"); - StackMemoryManager_Init(&app_workspace, g_aot_memory, WORKSPACE_SIZE); - - printf("Setting Inputs\n"); - struct tvmgen_default_inputs inputs = { - .ETHOSU_TEST_RUNNER_INPUT = input_data0, - }; - printf("Setting Outputs\n"); - struct tvmgen_default_outputs outputs = { - .output = output_data0, - }; - printf("Running inference\n"); - tvmgen_default_run(&inputs, &outputs); - - printf("Comparing output with expected output. Tolerance = %d\n", ETHOSU_TEST_RUNNER_TOL); - unsigned int error_count = 0; - for (unsigned int i = 0; i < expected_output_data0_len; ++i) { - int abs_err = abs(output_data0[i] - expected_output_data0[i]); - if (abs_err > ETHOSU_TEST_RUNNER_TOL) { - if (error_count < MAXDUMP) { - printf("Got %d, Expected %d at Index %d\n", output_data0[i], expected_output_data0[i], i); - } - ++error_count; - } - } - if (error_count > 0) { - result = 1; - } - - if (result == 0) { - printf("Output matches expected output\n"); - } - - printf("EXITTHESIM\n"); - while (1 == 1) - ; - return 0; -} diff --git a/tests/python/contrib/test_ethosu/fvp_test_runner/platform.ld b/tests/python/contrib/test_ethosu/fvp_test_runner/platform.ld deleted file mode 100644 index 087be19f329ba..0000000000000 --- a/tests/python/contrib/test_ethosu/fvp_test_runner/platform.ld +++ /dev/null @@ -1,302 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -/*------------------ Arm® Corstone™-300 Reference System Memories ------------- - - +===================+============+=======+===============+==================+ - | Memory | Address | Size | CPU Access | NPU Access | - +===================+============+=======+===============+==================+ - | ITCM | 0x00000000 | 512KB | Yes (RO) | No | - +-------------------+------------+-------+---------------+------------------+ - | DTCM | 0x20000000 | 512KB | Yes (R/W) | No | - +-------------------+------------+-------+---------------+------------------+ - | SSE-300 SRAM | 0x21000000 | 2MB | Yes (R/W) | Yes (R/W) | - +-------------------+------------+-------+---------------+------------------+ - | Data SRAM | 0x01000000 | 2MB | Yes (R/W) | Yes (R/W) | - +-------------------+------------+-------+---------------+------------------+ - | DDR | 0x60000000 | 32MB | Yes (R/W) | Yes (R/W) | - +-------------------+------------+-------+---------------+------------------+ */ - -/*---------------------- ITCM Configuration ---------------------------------- - Flash Configuration - Flash Base Address <0x0-0xFFFFFFFF:8> - Flash Size (in Bytes) <0x0-0xFFFFFFFF:8> - - -----------------------------------------------------------------------------*/ -__ROM_BASE = 0x00000000; -__ROM_SIZE = 0x00080000; - -/*--------------------- DTCM RAM Configuration ---------------------------- - RAM Configuration - RAM Base Address <0x0-0xFFFFFFFF:8> - RAM Size (in Bytes) <0x0-0xFFFFFFFF:8> - - -----------------------------------------------------------------------------*/ -__RAM_BASE = 0x20000000; -__RAM_SIZE = 0x00080000; - -/*----------------------- Data SRAM Configuration ------------------------------ - Data SRAM Configuration - DATA_SRAM Base Address <0x0-0xFFFFFFFF:8> - DATA_SRAM Size (in Bytes) <0x0-0xFFFFFFFF:8> - - -----------------------------------------------------------------------------*/ -__DATA_SRAM_BASE = 0x01000000; -__DATA_SRAM_SIZE = 0x00200000; - -/*--------------------- Embedded SRAM Configuration ---------------------------- - SRAM Configuration - SRAM Base Address <0x0-0xFFFFFFFF:8> - SRAM Size (in Bytes) <0x0-0xFFFFFFFF:8> - - -----------------------------------------------------------------------------*/ -__SRAM_BASE = 0x21000000; -__SRAM_SIZE = 0x00200000; - -/*--------------------- Stack / Heap Configuration ---------------------------- - Stack / Heap Configuration - Stack Size (in Bytes) <0x0-0xFFFFFFFF:8> - Heap Size (in Bytes) <0x0-0xFFFFFFFF:8> - - -----------------------------------------------------------------------------*/ -__STACK_SIZE = 0x00008000; -__HEAP_SIZE = 0x00008000; - -/*--------------------- Embedded RAM Configuration ---------------------------- - DDR Configuration - DDR Base Address <0x0-0xFFFFFFFF:8> - DDR Size (in Bytes) <0x0-0xFFFFFFFF:8> - - -----------------------------------------------------------------------------*/ -__DDR_BASE = 0x60000000; -__DDR_SIZE = 0x02000000; - -/* - *-------------------- <<< end of configuration section >>> ------------------- - */ - -MEMORY -{ - ITCM (rx) : ORIGIN = __ROM_BASE, LENGTH = __ROM_SIZE - DTCM (rwx) : ORIGIN = __RAM_BASE, LENGTH = __RAM_SIZE - DATA_SRAM (rwx) : ORIGIN = __DATA_SRAM_BASE, LENGTH = __DATA_SRAM_SIZE - SRAM (rwx) : ORIGIN = __SRAM_BASE, LENGTH = __SRAM_SIZE - DDR (rwx) : ORIGIN = __DDR_BASE, LENGTH = __DDR_SIZE -} - -/* Linker script to place sections and symbol values. Should be used together - * with other linker script that defines memory regions ITCM and RAM. - * It references following symbols, which must be defined in code: - * Reset_Handler : Entry of reset handler - * - * It defines following symbols, which code can use without definition: - * __exidx_start - * __exidx_end - * __copy_table_start__ - * __copy_table_end__ - * __zero_table_start__ - * __zero_table_end__ - * __etext - * __data_start__ - * __preinit_array_start - * __preinit_array_end - * __init_array_start - * __init_array_end - * __fini_array_start - * __fini_array_end - * __data_end__ - * __bss_start__ - * __bss_end__ - * __end__ - * end - * __HeapLimit - * __StackLimit - * __StackTop - * __stack - */ -ENTRY(Reset_Handler) - -SECTIONS -{ - .text : - { - KEEP(*(.vectors)) - *(.text*) - - KEEP(*(.init)) - KEEP(*(.fini)) - - /* .ctors */ - *crtbegin.o(.ctors) - *crtbegin?.o(.ctors) - *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) - *(SORT(.ctors.*)) - *(.ctors) - - /* .dtors */ - *crtbegin.o(.dtors) - *crtbegin?.o(.dtors) - *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) - *(SORT(.dtors.*)) - *(.dtors) - - *(.rodata*) - - KEEP(*(.eh_frame*)) - } > ITCM - - .ARM.extab : - { - *(.ARM.extab* .gnu.linkonce.armextab.*) - } > ITCM - - __exidx_start = .; - .ARM.exidx : - { - *(.ARM.exidx* .gnu.linkonce.armexidx.*) - } > ITCM - __exidx_end = .; - - .copy.table : - { - . = ALIGN(4); - __copy_table_start__ = .; - LONG (__etext) - LONG (__data_start__) - LONG (__data_end__ - __data_start__) - /* Add each additional data section here */ - __copy_table_end__ = .; - } > ITCM - - .zero.table : - { - . = ALIGN(4); - __zero_table_start__ = .; - __zero_table_end__ = .; - } > ITCM - - /** - * Location counter can end up 2byte aligned with narrow Thumb code but - * __etext is assumed by startup code to be the LMA of a section in DTCM - * which must be 4byte aligned - */ - __etext = ALIGN (4); - - .data : AT (__etext) - { - __data_start__ = .; - *(vtable) - *(.data) - *(.data.*) - - . = ALIGN(4); - /* preinit data */ - PROVIDE_HIDDEN (__preinit_array_start = .); - KEEP(*(.preinit_array)) - PROVIDE_HIDDEN (__preinit_array_end = .); - - . = ALIGN(4); - /* init data */ - PROVIDE_HIDDEN (__init_array_start = .); - KEEP(*(SORT(.init_array.*))) - KEEP(*(.init_array)) - PROVIDE_HIDDEN (__init_array_end = .); - - - . = ALIGN(4); - /* finit data */ - PROVIDE_HIDDEN (__fini_array_start = .); - KEEP(*(SORT(.fini_array.*))) - KEEP(*(.fini_array)) - PROVIDE_HIDDEN (__fini_array_end = .); - - KEEP(*(.jcr*)) - . = ALIGN(4); - /* All data end */ - __data_end__ = .; - - } > DTCM - - .sram : - { - . = ALIGN(16); - *(.bss.ethosu_fast_memory); - . = ALIGN(16); - } > SRAM AT > SRAM - - .bss.NoInit : - { - . = ALIGN(16); - *(.bss.NoInit) - . = ALIGN(16); - } > DDR AT > DDR - - .bss : - { - . = ALIGN(4); - __bss_start__ = .; - *(.bss) - *(.bss.*) - *(COMMON) - . = ALIGN(4); - __bss_end__ = .; - } > DTCM AT > DTCM - - .ddr : - { - . = ALIGN(4); - *(input_data_sec) - . = ALIGN(16); - *(crt_memory_sec) - *(weights_sec) - *(cms_data_sec) - *(graph_sec) - *(expected_output_data_sec) - *(output_data_sec) - . = ALIGN (16); - } > DDR - - .data_sram : - { - . = ALIGN(16); - } > DATA_SRAM - - .heap (COPY) : - { - . = ALIGN(8); - __end__ = .; - PROVIDE(end = .); - . = . + __HEAP_SIZE; - . = ALIGN(8); - __HeapLimit = .; - } > DTCM - - .stack (ORIGIN(DTCM) + LENGTH(DTCM) - __STACK_SIZE) (COPY) : - { - . = ALIGN(8); - __StackLimit = .; - . = . + __STACK_SIZE; - . = ALIGN(8); - __StackTop = .; - } > DTCM - PROVIDE(__stack = __StackTop); - - /* Check if data + stack exceeds DTCM limit */ - ASSERT(__StackLimit >= __bss_end__, "region DTCM overflowed with stack") -} diff --git a/tests/python/contrib/test_ethosu/fvp_test_runner/run_fvp.sh b/tests/python/contrib/test_ethosu/fvp_test_runner/run_fvp.sh deleted file mode 100755 index 520d14b311d2d..0000000000000 --- a/tests/python/contrib/test_ethosu/fvp_test_runner/run_fvp.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/bash -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -# Runs executables the on the Arm® Corstone™-300 Reference System -# -# Usage: run_fvp.sh - -set -e - -/opt/arm/FVP_Corstone_SSE-300_Ethos-U55/models/Linux64_GCC-6.4/FVP_Corstone_SSE-300_Ethos-U55 -C cpu0.CFGDTCMSZ=15 \ --C cpu0.CFGITCMSZ=15 -C mps3_board.uart0.out_file=\"-\" -C mps3_board.uart0.shutdown_tag=\"EXITTHESIM\" \ --C mps3_board.visualisation.disable-visualisation=1 -C mps3_board.telnetterminal0.start_telnet=0 \ --C mps3_board.telnetterminal1.start_telnet=0 -C mps3_board.telnetterminal2.start_telnet=0 -C mps3_board.telnetterminal5.start_telnet=0 \ --C ethosu.extra_args="--fast" \ --C ethosu.num_macs=$1 $2 \ No newline at end of file