diff --git a/examples/.gitignore b/examples/.gitignore index 2c55a9bd9..1b61de35f 100644 --- a/examples/.gitignore +++ b/examples/.gitignore @@ -24,3 +24,6 @@ ucli.key *.saifgen *.json dramsim3.txt +*.riscv +*.elf + diff --git a/examples/library/test_manycore_packets/main.c b/examples/library/test_manycore_packets/main.c index 8225ec827..ff8b48397 100644 --- a/examples/library/test_manycore_packets/main.c +++ b/examples/library/test_manycore_packets/main.c @@ -193,4 +193,4 @@ int test_manycore_packets(int argc, char *argv[]) { return HB_MC_SUCCESS; } -declare_program_main(basename(__FILE__), test_manycore_packets); +declare_program_main("test_manycore_packets", test_manycore_packets); diff --git a/examples/spmd/loader.c b/examples/spmd/loader.c index fa1a5a486..95ef48449 100644 --- a/examples/spmd/loader.c +++ b/examples/spmd/loader.c @@ -33,13 +33,38 @@ #include #include -#include +//#include #include #include #include #include #include +/* + * Reads manycore specific symbols from the program binary + * @param[out] program: Pointer to a memory location containing the address of the manycore binary + * @param[out] program_size: Pointer to the manycore binary size + * @returns HB_MC_SUCCESS + */ +static int read_symbols(unsigned char **program, size_t *program_size) { + unsigned char *address; + uint64_t size; + + // Read the address from the symbol table in the binary + __asm__ __volatile__ ("la %[manycore_start_addr], manycore" + : [manycore_start_addr] "=r" (address)); + + // The program size is already stored at a predefined location in the binary + __asm__ __volatile__ ("la t0, manycore_size\n\t" + "ld %[manycore_size], 0(t0)" + : [manycore_size] "=r" (size)); + + *program_size = size; + *program = address; + + return HB_MC_SUCCESS; +} + static int read_program_file(const char *file_name, unsigned char **file_data, size_t *file_size) { struct stat st; @@ -97,7 +122,8 @@ int test_loader(int argc, char **argv) { // read in the program data from the file system - err = read_program_file(bin_path, &program_data, &program_size); + // err = read_program_file(bin_path, &program_data, &program_size); + err = read_symbols(&program_data, &program_size); if (err != HB_MC_SUCCESS) return err; diff --git a/libraries/.gitignore b/libraries/.gitignore index 8b447aa5d..437ff9063 100644 --- a/libraries/.gitignore +++ b/libraries/.gitignore @@ -1,4 +1,8 @@ *.o *.so *.so.1 -*.so.1.0 \ No newline at end of file +*.so.1.0 +*.riscv +*.a +*.a.1 +*.a.1.0 diff --git a/libraries/bsg_manycore_epa.h b/libraries/bsg_manycore_epa.h index 8abf94a85..00bdd386a 100644 --- a/libraries/bsg_manycore_epa.h +++ b/libraries/bsg_manycore_epa.h @@ -62,7 +62,12 @@ extern "C" { #define HB_MC_GLOBAL_EPA_LOGSZ 16 #define HB_MC_HOST_EPA_FINISH 0xEAD0 +#define HB_MC_HOST_EPA_TIME 0xEAD4 #define HB_MC_HOST_EPA_FAIL 0xEAD8 +#define HB_MC_HOST_EPA_STDOUT 0xEADC +#define HB_MC_HOST_EPA_STDERR 0xEAE0 +#define HB_MC_HOST_EPA_BRANCH_TRACE 0xEAE4 +#define HB_MC_HOST_EPA_PRINT_STAT 0xEA0C #define EPA_FROM_BASE_AND_OFFSET(base, offset) \ (((base)+(offset))) diff --git a/libraries/features/dma/noimpl/feature.mk b/libraries/features/dma/noimpl/feature.mk index 84aa9b7b3..9117e4436 100644 --- a/libraries/features/dma/noimpl/feature.mk +++ b/libraries/features/dma/noimpl/feature.mk @@ -40,6 +40,8 @@ $(DMA_FEATURE_OBJECTS): CXXFLAGS := -std=c++11 -fPIC -D_GNU_SOURCE -D_DEFAULT_SO $(BSG_PLATFORM_PATH)/libbsg_manycore_runtime.so.1.0: $(DMA_FEATURE_OBJECTS) +$(BSG_PLATFORM_PATH)/libbsg_manycore_runtime.a: $(DMA_FEATURE_OBJECTS) + .PHONY: dma_feature.clean dma_feature.clean: rm -f $(DMA_FEATURE_OBJECTS) diff --git a/libraries/features/dma/simulation/feature.mk b/libraries/features/dma/simulation/feature.mk index 07829f913..a4f851a26 100644 --- a/libraries/features/dma/simulation/feature.mk +++ b/libraries/features/dma/simulation/feature.mk @@ -46,6 +46,7 @@ $(DMA_FEATURE_OBJECTS): CXXFLAGS := -std=c++11 -fPIC $(INCLUDES) -D_GNU_SOURCE - $(BSG_PLATFORM_PATH)/libbsg_manycore_runtime.so.1.0: $(DMA_FEATURE_OBJECTS) +$(BSG_PLATFORM_PATH)/libbsg_manycore_runtime.a: $(DMA_FEATURE_OBJECTS) .PHONY: dma_feature.clean dma_feature.clean: diff --git a/libraries/features/dma/simulation/libdmamem.mk b/libraries/features/dma/simulation/libdmamem.mk index 149d5da17..9d545fc9a 100644 --- a/libraries/features/dma/simulation/libdmamem.mk +++ b/libraries/features/dma/simulation/libdmamem.mk @@ -60,7 +60,7 @@ dmamem.clean: libraries.clean: dmamem.clean # Rules for building Simulation "DMA library" -$(LIBRARIES_PATH)/features/dma/simulation/libdmamem.so: CXXFLAGS += -std=c++11 -D_GNU_SOURCE -D_DEFAULT_SOURCE -Wall -fPIC -shared +$(LIBRARIES_PATH)/features/dma/simulation/libdmamem.so: CXXFLAGS := -std=c++11 -D_GNU_SOURCE -D_DEFAULT_SOURCE -Wall -fPIC -shared $(LIBRARIES_PATH)/features/dma/simulation/libdmamem.so: CXXFLAGS += -I$(BASEJUMP_STL_DIR)/bsg_mem $(LIBRARIES_PATH)/features/dma/simulation/libdmamem.so: CXXFLAGS += -DBASEJUMP_STL_DIR="$(BASEJUMP_STL_DIR)" $(LIBRARIES_PATH)/features/dma/simulation/libdmamem.so: CXX=g++ diff --git a/libraries/libraries.mk b/libraries/libraries.mk index ebc756fa3..23604a7e8 100644 --- a/libraries/libraries.mk +++ b/libraries/libraries.mk @@ -28,6 +28,7 @@ ifndef __BSG_LIBRARIES_MK __BSG_LIBRARIES_MK := 1 +LIB_SSOURCES += LIB_CSOURCES += LIB_CSOURCES += $(LIBRARIES_PATH)/bsg_manycore_config_id_to_string.c LIB_CSOURCES += $(LIBRARIES_PATH)/bsg_manycore_memsys.c @@ -99,6 +100,7 @@ LIB_DEBUG_OBJECTS += LIB_OBJECTS += $(patsubst %cpp,%o,$(LIB_CXXSOURCES)) LIB_OBJECTS += $(patsubst %c,%o,$(LIB_CSOURCES)) +LIB_OBJECTS += $(patsubst %S,%o,$(LIB_SSOURCES)) LIB_OBJECTS_CUDA_POD_REPL += $(patsubst %cpp,%o,$(LIB_CXXSOURCES_CUDA_POD_REPL)) LIB_OBJECTS_REGRESSION += $(patsubst %cpp,%o,$(LIB_CXXSOURCES_REGRESSION)) @@ -114,10 +116,14 @@ $(LIB_OBJECTS) $(LIB_OBJECTS_CUDA_POD_REPL) $(LIB_OBJECTS_REGRESSION): INCLUDES $(LIB_OBJECTS) $(LIB_OBJECTS_CUDA_POD_REPL) $(LIB_OBJECTS_REGRESSION): INCLUDES += -I$(LIBRARIES_PATH)/xcl $(LIB_OBJECTS) $(LIB_OBJECTS_CUDA_POD_REPL) $(LIB_OBJECTS_REGRESSION): INCLUDES += -I$(LIBRARIES_PATH)/features/dma $(LIB_OBJECTS) $(LIB_OBJECTS_CUDA_POD_REPL) $(LIB_OBJECTS_REGRESSION): INCLUDES += -I$(LIBRARIES_PATH)/features/profiler +$(LIB_OBJECTS) $(LIB_OBJECTS_CUDA_POD_REPL) $(LIB_OBJECTS_REGRESSION): INCLUDES += -I$(LIBRARIES_PATH)/features/tracer $(LIB_OBJECTS) $(LIB_OBJECTS_CUDA_POD_REPL) $(LIB_OBJECTS_REGRESSION): INCLUDES += -I$(BSG_PLATFORM_PATH) +# A way to add include paths for the platform; This can't be a part of the platform's library.mk because of the overriding +# definition of the variables in this flow. +$(LIB_OBJECTS) $(LIB_OBJECTS_CUDA_POD_REPL) $(LIB_OBJECTS_REGRESSION): INCLUDES += $(LIB_PLATFORM_INCLUDES) -$(LIB_OBJECTS) $(LIB_OBJECTS_CUDA_POD_REPL) $(LIB_OBJECTS_REGRESSION): CFLAGS += -std=c11 -fPIC $(INCLUDES) -D_GNU_SOURCE -D_BSD_SOURCE -D_DEFAULT_SOURCE -$(LIB_OBJECTS) $(LIB_OBJECTS_CUDA_POD_REPL) $(LIB_OBJECTS_REGRESSION): CXXFLAGS += -std=c++11 -fPIC $(INCLUDES) -D_GNU_SOURCE -D_BSD_SOURCE -D_DEFAULT_SOURCE +$(LIB_OBJECTS) $(LIB_OBJECTS_CUDA_POD_REPL) $(LIB_OBJECTS_REGRESSION): CFLAGS += -O3 -std=c11 -fPIC -D_GNU_SOURCE -D_BSD_SOURCE -D_DEFAULT_SOURCE +$(LIB_OBJECTS) $(LIB_OBJECTS_CUDA_POD_REPL) $(LIB_OBJECTS_REGRESSION): CXXFLAGS += -O3 -std=c++11 -fPIC -D_GNU_SOURCE -D_BSD_SOURCE -D_DEFAULT_SOURCE # Need to move this, eventually #$(LIB_OBJECTS) $(PLATFORM_OBJECTS): $(BSG_MACHINE_PATH)/bsg_manycore_machine.h @@ -135,21 +141,37 @@ $(BSG_PLATFORM_PATH)/libbsg_manycore_runtime.so.1.0: $(LIB_OBJECTS) $(BSG_PLATFORM_PATH)/libbsgmc_cuda_legacy_pod_repl.so.1.0: LDFLAGS := $(BSG_PLATFORM_PATH)/libbsgmc_cuda_legacy_pod_repl.so.1.0: INCLUDES := + $(BSG_PLATFORM_PATH)/libbsgmc_cuda_legacy_pod_repl.so.1.0: LD = $(CXX) $(BSG_PLATFORM_PATH)/libbsgmc_cuda_legacy_pod_repl.so.1.0: $(LIB_OBJECTS_CUDA_POD_REPL) $(LD) -shared -Wl,-soname,$(basename $(notdir $@)) -o $@ $^ $(LDFLAGS) $(BSG_PLATFORM_PATH)/libbsg_manycore_regression.so.1.0: LDFLAGS := $(BSG_PLATFORM_PATH)/libbsg_manycore_regression.so.1.0: INCLUDES := + $(BSG_PLATFORM_PATH)/libbsg_manycore_regression.so.1.0: LD = $(CXX) $(BSG_PLATFORM_PATH)/libbsg_manycore_regression.so.1.0: $(LIB_OBJECTS_REGRESSION) $(LD) -shared -Wl,-soname,$(basename $(notdir $@)) -o $@ $^ $(LDFLAGS) +$(BSG_PLATFORM_PATH)/libbsg_manycore_runtime.a: $(LIB_OBJECTS) + $(AR) -rcs $@ $^ + +$(BSG_PLATFORM_PATH)/libbsgmc_cuda_legacy_pod_repl.a: $(LIB_OBJECTS_CUDA_POD_REPL) + $(AR) -rcs $@ $^ + +$(BSG_PLATFORM_PATH)/libbsg_manycore_regression.a: $(LIB_OBJECTS_REGRESSION) + $(AR) -rcs $@ $^ + .PHONY: libraries.clean libraries.clean: rm -f $(LIB_OBJECTS) + rm -f $(LIB_OBJECTS_CUDA_POD_REPL) + rm -f $(LIB_OBJECTS_REGRESSION) rm -f $(BSG_PLATFORM_PATH)/libbsg_manycore_runtime.so.1.0 rm -f $(BSG_PLATFORM_PATH)/libbsgmc_cuda_legacy_pod_repl.so.1.0 rm -f $(BSG_PLATFORM_PATH)/libbsg_manycore_regression.so.1.0 + rm -f $(BSG_PLATFORM_PATH)/libbsg_manycore_runtime.a + rm -f $(BSG_PLATFORM_PATH)/libbsgmc_cuda_legacy_pod_repl.a + rm -f $(BSG_PLATFORM_PATH)/libbsg_manycore_regression.a endif diff --git a/libraries/platforms/dromajo-vcs/.gitignore b/libraries/platforms/dromajo-vcs/.gitignore new file mode 100644 index 000000000..fbd4b459f --- /dev/null +++ b/libraries/platforms/dromajo-vcs/.gitignore @@ -0,0 +1,2 @@ +lfs.cpp +software/linker/riscv.ld diff --git a/libraries/platforms/dromajo-vcs/README.md b/libraries/platforms/dromajo-vcs/README.md new file mode 100644 index 000000000..1c1f15f6d --- /dev/null +++ b/libraries/platforms/dromajo-vcs/README.md @@ -0,0 +1,76 @@ +# Dromajo HammerBlade Platform on VCS + +## Introduction + +The Dromajo-HammerBlade platform is a precursor to the BlackParrot-HammerBlade platform a.k.a HammerParrot. It builds on the traditional x86 setup and replaces x86 with Dromajo as the main host (except for a few operations) that executes the CUDA-lite program. Broadly speaking there are three main components to the entire system + +- x86: It is the base platform on which the entire simulation infrastructure is built. In a traditional setup, the x86 core runs the CUDA-lite binary while simulating the manycore hardware. In this case, the x86 core instead runs the Dromajo emulator in addition to simulating the manycore hardware whilst performing some host-like operations and interacting with the hardware through DPI. +- Dromajo: A C++ based 64-bit RISC-V processor emulator developed by Esperanto Technologies with modifications to communicate with the HammerBlade manycore. All CUDA-lite host code is executed by a RISC-V processor emulated by Dromajo. Since, HammerParrot is the eventual goal, Dromajo has been modified to work like BlackParrot. +- HammerBlade hardware: Like all other platforms, the manycore hardware consists of the manycore with a specific machine configuration and is equipped with DPI FIFOs on the west side forming the manycore bridge. + +## Dependencies + +### BlackParrot SDK + +The [BlackParrot SDK](https://github.com/black-parrot-sdk/black-parrot-sdk) contains multiple submodules +- [Dromajo](https://github.com/bsg-external/dromajo/tree/4cbf5d91b880e172f15e0658cc72d4d6426ddcaa), the 64-bit RISC-V emulator +- [Perch](https://github.com/black-parrot-sdk/perch/tree/f3a302b3a902e80952aa8b3b8649713398d1b749), the BlackParrot firmware +- [PanicRoom](https://github.com/bespoke-silicon-group/bsg_newlib_dramfs/tree/28b5ac5a75847f346b54a91d4b9b9f58a62b590e), a RISC-V Newlib compiler with support for some system calls and a minimal filesystem. + +This directory must be placed at the same level as the replicant repository. It is recommended that users clone the Bladerunner meta repository which includes (or will include) the BlackParrot SDK as a submodule [(PR #66)](https://github.com/bespoke-silicon-group/bsg_bladerunner/pull/66) since this will set all the required environment variables correctly. Otherwise users must define the BLACKPARROT_SDK_DIR variable for the given execution environment. + +## Technical Reference Manual + +Click [here](https://docs.google.com/document/d/1kUtyD3SiXP2qvbUDhtpx_eFufegscQs2cWla3pcxA-Q/edit?usp=sharing) to access the technical reference manual. + +## Using the Simulation Infrastructure + +The simulation infrastructure can be used just like any other platform. An example is given below. + +``` +cd examples/library/test_coordinate +make main.exec.log +``` + +This will build the libraries and the platform binaries, compile and elaborate the hardware and run the test program. The example output for the above program should look as given below + +``` +BSG INFO: Regression Test: test_coordinate +INFO: Starting test foreach_coordinate +INFO: iteration 0: (0,0) +INFO: Starting test foreach_x_y +INFO: iteration 0: (0,0) +INFO: Starting test foreach_coordinate +INFO: iteration 0: (0,0) +INFO: iteration 1: (1,0) +INFO: Starting test foreach_x_y +INFO: iteration 0: (0,0) +INFO: iteration 1: (1,0) +INFO: Starting test foreach_coordinate +INFO: iteration 0: (0,0) +INFO: iteration 1: (0,1) +INFO: Starting test foreach_x_y +INFO: iteration 0: (0,0) +INFO: iteration 1: (0,1) +INFO: Starting test foreach_coordinate +INFO: iteration 0: (0,2) +INFO: iteration 1: (0,3) +INFO: iteration 2: (1,2) +INFO: iteration 3: (1,3) +INFO: Starting test foreach_x_y +INFO: iteration 0: (0,2) +INFO: iteration 1: (0,3) +INFO: iteration 2: (1,2) +INFO: iteration 3: (1,3) +BSG REGRESSION TEST PASSED +INFO: Core 0 successfully terminated +BSG REGRESSION TEST PASSED +BSG COSIM PASS: Test passed! +``` + +### Notes + +- This infrastructure has only been tested with tests in `examples/library`. SPMD and CUDA-lite testing is still remaining. +- All testing has been carried out for a single pod configuration. +- Only the `exec` make target has been tested. The `saif` and `debug` targets still remain to be tested. However, since this pertains to gathering metrics with respect to the hardware being simulated, there is a fair amount of confidence that it will work right out of the box. +- The simulation takes a very long time even for some of the library tests. This can be attributed to the fact that Dromajo is used in co-simulation mode (one instruction per time step) with code executing on the x86 core controlling the notion of time for the whole system. Dromajo also has a standalone mode in which case it must become the driver of time for the whole system. While the reason mentioned here should be verified after an in-depth performance analysis of the system, it is still worthwhile to explore Dromajo in the standalone mode. This is one of major barriers to running CUDA-lite test programs and completing its execution in a reasonable time frame. diff --git a/libraries/platforms/dromajo-vcs/bsg_manycore_platform.cpp b/libraries/platforms/dromajo-vcs/bsg_manycore_platform.cpp new file mode 100644 index 000000000..f691a43ec --- /dev/null +++ b/libraries/platforms/dromajo-vcs/bsg_manycore_platform.cpp @@ -0,0 +1,560 @@ +// Copyright (c) 2020, University of Washington All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// Redistributions of source code must retain the above copyright notice, this list +// of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above copyright notice, this +// list of conditions and the following disclaimer in the documentation and/or +// other materials provided with the distribution. +// +// Neither the name of the copyright holder nor the names of its contributors may +// be used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include +#include +#include +#include + +#include + +#include + +/* these are convenience macros that are only good for one line prints */ +#define manycore_pr_dbg(mc, fmt, ...) \ + bsg_pr_dbg("%s: " fmt, mc->name, ##__VA_ARGS__) + +#define manycore_pr_err(mc, fmt, ...) \ + bsg_pr_err("%s: " fmt, mc->name, ##__VA_ARGS__) + +#define manycore_pr_warn(mc, fmt, ...) \ + bsg_pr_warn("%s: " fmt, mc->name, ##__VA_ARGS__) + +#define manycore_pr_info(mc, fmt, ...) \ + bsg_pr_info("%s: " fmt, mc->name, ##__VA_ARGS__) + +/********************************** BlackParrot platform API **********************************/ + +/* + * Reads the manycore bridge for number of credits used in the endpoint + * @param[in] credits_used --> Pointer to a location in memory that will hold the number of credits used + * @returns HB_MC_SUCCESS + */ +int hb_bp_get_credits_used(int *credits_used) { + uint32_t *bp_to_mc_req_credits_addr = reinterpret_cast(MC_BASE_ADDR + BP_TO_MC_REQ_CREDITS_ADDR); + *credits_used = (int) (*bp_to_mc_req_credits_addr); + if (*credits_used < 0) { + bsg_pr_err("Credits used cannot be negative. Credits used = %d\n", *credits_used); + return HB_MC_FAIL; + } + return HB_MC_SUCCESS; +} + +/* + * Writes a 128-bit manycore packet in 32-bit chunks to the manycore bridge FIFO + * @param[in] pkt --> Pointer to the manycore packet + * @returns HB_MC_SUCCESS + */ +int hb_bp_write_to_mc_bridge(hb_mc_packet_t *pkt) { + uint32_t *bp_to_mc_req_fifo_addr = reinterpret_cast(MC_BASE_ADDR + BP_TO_MC_REQ_FIFO_ADDR); + // TODO: Currently this function maps to an operation in Dromajo that has to wait until the write succeeds. + // The Dromajo function must do some error checking and return appropriate error codes to the software. + // However, currently (and understandbly) Dromajo does not allow anything to be returned on writes to + // the software. + for(int i = 0; i < 4; i++) { + *bp_to_mc_req_fifo_addr = pkt->words[i]; + bp_to_mc_req_fifo_addr++; + } + return HB_MC_SUCCESS; +} + +/* + * Checks if the MC to BP FIFO contains any entries + * @param[in] entries --> Pointer to a location in memory that will hold the number of entries + * @param[in] type --> Type of FIFO to poll for entries + * @returns number of entries in the MC to BP FIFO + */ +int hb_bp_get_rx_fifo_entries(int *entries, hb_mc_fifo_rx_t type) { + uint32_t *addr; + switch (type) { + case HB_MC_FIFO_RX_REQ: addr = reinterpret_cast(MC_BASE_ADDR + MC_TO_BP_REQ_ENTRIES_ADDR); + break; + case HB_MC_FIFO_RX_RSP: addr = reinterpret_cast(MC_BASE_ADDR + MC_TO_BP_RESP_ENTRIES_ADDR); + break; + default: + { + bsg_pr_err("%s: Unknown FIFO type\n", __func__); + return HB_MC_FAIL; + } + break; + } + + *entries = *addr; + if (*entries < 0) { + bsg_pr_err("Entries occupied cannot be negative. Entries = %d\n", *entries); + return HB_MC_FAIL; + } + + return HB_MC_SUCCESS; +} + +/* + * Checks if the BP to MC FIFO contains any entries + * @param[in] entries --> Pointer to a location in memory that will hold the number of entries + * @param[in] type --> Type of FIFO to poll for entries + * @returns number of entries in the BP to MC FIFO + */ +int hb_bp_get_tx_fifo_entries(int *entries, hb_mc_fifo_tx_t type) { + uint32_t *addr; + switch (type) { + case HB_MC_FIFO_TX_REQ: addr = reinterpret_cast(MC_BASE_ADDR + BP_TO_MC_REQ_ENTRIES_ADDR); + break; + default: + { + bsg_pr_err("%s: Unknown FIFO type\n", __func__); + return HB_MC_FAIL; + } + break; + } + + *entries = *addr; + if (*entries < 0) { + bsg_pr_err("Entries occupied cannot be negative. Entries = %d\n", *entries); + return HB_MC_FAIL; + } + + return HB_MC_SUCCESS; +} + +/* + * Reads the manycore bridge FIFOs in 32-bit chunks to form the 128-bit packet + * @param[in] pkt --> Pointer to the manycore packet + * @param[in] type --> Type of FIFO to read from + * @returns HB_MC_SUCCESS on success, HB_MC_FAIL if FIFO type is unknown + */ +int hb_bp_read_from_mc_bridge(hb_mc_packet_t *pkt, hb_mc_fifo_rx_t type) { + uint32_t *addr; + switch(type) { + case HB_MC_FIFO_RX_REQ: addr = reinterpret_cast(MC_BASE_ADDR + MC_TO_BP_REQ_FIFO_ADDR); + break; + case HB_MC_FIFO_RX_RSP: addr = reinterpret_cast(MC_BASE_ADDR + MC_TO_BP_RESP_FIFO_ADDR); + break; + default: + { + bsg_pr_err("%s: Unknown packet type\n", __func__); + return HB_MC_FAIL; + } + break; + } + + uint32_t fifo_read_status = DROMAJO_RW_FAIL_CODE; + do { + for (int i = 0; i < 4; i++) { + pkt->words[i] = *addr; + fifo_read_status &= pkt->words[i]; + addr++; + } + } while (fifo_read_status == DROMAJO_RW_FAIL_CODE); + + // There is something wrong if the read status is equal to the FAIL code + if (fifo_read_status == DROMAJO_RW_FAIL_CODE) + return HB_MC_FAIL; + + return HB_MC_SUCCESS; +} + +/********************************** Manycore platform API **********************************/ + +typedef struct hb_mc_platform_t +{ + hb_mc_manycore_id_t id; +} hb_mc_platform_t; + +static std::set active_ids; + +/** + * Clean up the runtime platform + * @param[in] mc A manycore to clean up + */ +void hb_mc_platform_cleanup(hb_mc_manycore_t *mc) { + hb_mc_platform_t *platform = reinterpret_cast(mc->platform); + + // Remove the key + auto key = active_ids.find(platform->id); + active_ids.erase(key); + + platform->id = 0; + + // Ideally, we would clean up each platform in + // platforms. However, we can't guarantee that someone won't + // call init again, so we'll live with the memory leak for + // now. It's a rare case, since few people call init/cleanup + // and then continue their program indefinitley. + + return; +} + +/** + * Initialize the runtime platform + * @param[in] mc A manycore to initialize + * @param[in] id ID which selects the physical hardware from which this manycore is configured + * @return HB_MC_FAIL if an error occured. HB_MC_SUCCESS otherwise. + */ +int hb_mc_platform_init(hb_mc_manycore_t *mc, hb_mc_manycore_id_t id) { + hb_mc_platform_t *platform = new hb_mc_platform_t; + + // check if mc is already initialized + if (mc->platform) + return HB_MC_INITIALIZED_TWICE; + + if (id != 0) + { + manycore_pr_err(mc, "Failed to init platform: invalid ID\n"); + return HB_MC_INVALID; + } + + // Check if the ID has already been initialized + if(active_ids.find(id) != active_ids.end()) + { + manycore_pr_err(mc, "Already initialized ID\n"); + return HB_MC_INVALID; + } + + active_ids.insert(id); + platform->id = id; + + mc->platform = reinterpret_cast(platform); + + return HB_MC_SUCCESS; +} + +/** + * Transmit a request packet to manycore hardware + * @param[in] mc A manycore instance initialized with hb_mc_manycore_init() + * @param[in] request A request packet to transmit to manycore hardware + * @param[in] timeout A timeout counter. Unused - set to -1 to wait forever. + * @return HB_MC_SUCCESS on success. Otherwise an error code defined in bsg_manycore_errno.h. + */ +int hb_mc_platform_transmit(hb_mc_manycore_t *mc, hb_mc_packet_t *packet, hb_mc_fifo_tx_t type, long timeout) { + hb_mc_platform_t *platform = reinterpret_cast(mc->platform); + const char *typestr = hb_mc_fifo_tx_to_string(type); + const hb_mc_config_t *cfg = hb_mc_manycore_get_config(mc); + uint32_t max_credits = hb_mc_config_get_transmit_vacancy_max(cfg); + int err; + + // Timeout is unsupported + if (timeout != -1) + { + manycore_pr_err(mc, "%s: Only a timeout value of -1 is supported\n", __func__); + return HB_MC_INVALID; + } + + // Host doesn't send responses to the mamycore + if (type == HB_MC_FIFO_TX_RSP) + { + manycore_pr_err(mc, "TX Response Not Supported!\n"); + return HB_MC_NOIMPL; + } + + // Don't need to check for error code since this operation should always be a success since it is not going + // over the network + int credits_used = 0; + do { + err = hb_bp_get_credits_used(&credits_used); + } while ((err != HB_MC_SUCCESS) || ((max_credits - credits_used) <= 0)); + + err = hb_bp_write_to_mc_bridge(packet); + if (err != HB_MC_SUCCESS) { + manycore_pr_err(mc, "%s: Write to the host request FIFO failed\n", __func__); + return err; + } + + return HB_MC_SUCCESS; +} + +/** + * Receive a packet from manycore hardware + * @param[in] mc A manycore instance initialized with hb_mc_manycore_init() + * @param[in] response A packet into which data should be read + * @param[in] timeout A timeout counter. Unused - set to -1 to wait forever. + * @return HB_MC_SUCCESS on success. Otherwise an error code defined in bsg_manycore_errno.h. + */ +int hb_mc_platform_receive(hb_mc_manycore_t *mc, hb_mc_packet_t *packet, hb_mc_fifo_rx_t type, long timeout) { + hb_mc_platform_t *platform = reinterpret_cast(mc->platform); + const char *typestr = hb_mc_fifo_rx_to_string(type); + + // Timeout is unsupported + if (timeout != -1) + { + manycore_pr_err(mc, "%s: Only a timeout value of -1 is supported\n", __func__); + return HB_MC_INVALID; + } + + int err; + int num_rx_entries = 0; + do { + err = hb_bp_get_rx_fifo_entries(&num_rx_entries, type); + } while ((num_rx_entries == 0) || (err != HB_MC_SUCCESS)); + + err = hb_bp_read_from_mc_bridge(packet, type); + if (err != HB_MC_SUCCESS) { + manycore_pr_err(mc, "%s: Read from the %s FIFO did not succeed\n", __func__, typestr); + return err; + } + + return HB_MC_SUCCESS; +} + +/** + * Read the configuration register at an index + * @param[in] mc A manycore instance initialized with hb_mc_manycore_init() + * @param[in] idx Configuration register index to access + * @param[out] config Configuration value at index + * @return HB_MC_SUCCESS on success. Otherwise an error code defined in bsg_manycore_errno.h. + */ +int hb_mc_platform_get_config_at(hb_mc_manycore_t *mc, unsigned int idx, hb_mc_config_raw_t *config) { + hb_mc_platform_t *platform = reinterpret_cast(mc->platform); + hb_mc_packet_t config_req_pkt, config_resp_pkt; + int num_rx_entries = 0; + int err; + + config_req_pkt.request.x_dst = HOST_X_COORD; + config_req_pkt.request.y_dst = HOST_Y_COORD; + config_req_pkt.request.x_src = BP_HOST_LINK_X; + config_req_pkt.request.y_src = BP_HOST_LINK_Y; + config_req_pkt.request.op_v2 = HB_MC_PACKET_OP_REMOTE_LOAD; + config_req_pkt.request.payload = 0; + config_req_pkt.request.reg_id = 0; + // Note: The onus is on the simulator (i.e. x86 code) to check if the config + // index is within bounds before accessing the DPI ROM. + config_req_pkt.request.addr = HB_BP_HOST_EPA_CONFIG_START + (idx << 2); + + // Note: Potentially dangerous to write to the FIFO without checking for credits + // We get back credits used and not credits remaining and without the configuration + // there is no way to know the credits remaining. + err = hb_bp_write_to_mc_bridge(&config_req_pkt); + if (err != HB_MC_SUCCESS) { + manycore_pr_err(mc, "%s: Write to the host request FIFO failed\n", __func__); + return err; + } + + do { + err = hb_bp_get_rx_fifo_entries(&num_rx_entries, HB_MC_FIFO_RX_RSP); + } while ((num_rx_entries == 0) || (err != HB_MC_SUCCESS)); + + err = hb_bp_read_from_mc_bridge(&config_resp_pkt, HB_MC_FIFO_RX_RSP); + if (err != HB_MC_SUCCESS) { + manycore_pr_err(mc, "%s: Read from the manycore response FIFO failed\n", __func__); + return err; + } + + *config = config_resp_pkt.response.data; + + return HB_MC_SUCCESS; +} + +/** + * Stall until the all requests (and responses) have reached their destination. + * @param[in] mc A manycore instance initialized with hb_mc_manycore_init() + * @param[in] timeout A timeout counter. Unused - set to -1 to wait forever. + * @return HB_MC_SUCCESS on success. Otherwise an error code defined in bsg_manycore_errno.h. + */ +int hb_mc_platform_fence(hb_mc_manycore_t *mc, long timeout) { + int credits_used, is_vacant, num_rx_entries, num_tx_entries, err; + hb_mc_packet_t fence_req_pkt, fence_resp_pkt; + num_rx_entries = num_tx_entries = 0; + credits_used = 0; + + hb_mc_platform_t *platform = reinterpret_cast(mc->platform); + + if (timeout != -1) { + manycore_pr_err(mc, "%s: Only a timeout value of -1 is supported\n", __func__); + return HB_MC_NOIMPL; + } + + // 3 (probably 4) things need to happen during a fence - + // Used credits should be 0 + // The DPI TX FIFO should be empty + // The Dromajo TX FIFO should be empty + // The receiving FIFOs should also be empty + + // Prepare host packet to query TX vacancy + fence_req_pkt.request.x_dst = HOST_X_COORD; + fence_req_pkt.request.y_dst = HOST_Y_COORD; + fence_req_pkt.request.x_src = BP_HOST_LINK_X; + fence_req_pkt.request.y_src = BP_HOST_LINK_Y; + fence_req_pkt.request.op_v2 = HB_MC_PACKET_OP_REMOTE_LOAD; + fence_req_pkt.request.payload = 0; + fence_req_pkt.request.reg_id = 0; + fence_req_pkt.request.addr = HB_BP_HOST_EPA_TX_VACANT; // x86 Host address to poll tx vacancy + + do + { + err = hb_bp_get_credits_used(&credits_used); + if (err != HB_MC_SUCCESS) { + manycore_pr_err(mc, "%s: Credit update failed\n", __func__); + return err; + } + + // In the real system (with BP), we will be performing memory-mapped reads to the bridge to grab the current + // status of the number of endpoint credits for the host. With Dromajo, it still makes sense to do this since + // host packets are not transmitted over the network. If host packets have to go over the network then it does + // not make sense since we are sending packets on the DPI TX FIFO and are expecting the TX FIFO to be vacant. + err = hb_bp_write_to_mc_bridge(&fence_req_pkt); + if (err != HB_MC_SUCCESS) { + manycore_pr_err(mc, "%s: Write to the host request FIFO failed\n", __func__); + return err; + } + + do { + err = hb_bp_get_rx_fifo_entries(&num_rx_entries, HB_MC_FIFO_RX_RSP); + } while ((num_rx_entries == 0) || (err != HB_MC_SUCCESS)); + + err = hb_bp_read_from_mc_bridge(&fence_resp_pkt, HB_MC_FIFO_RX_RSP); + if (err != HB_MC_SUCCESS) { + manycore_pr_err(mc, "%s: Read from the manycore response FIFO failed\n", __func__); + return err; + } + + is_vacant = fence_resp_pkt.response.data; + } while ((credits_used != 0) || !is_vacant); + + // We made sure there are no credits being used (TX FIFO queries are host packets and don't use the network) and + // the DPI TX FIFO is empty. Also every time we perform a transmit, we drain the Dromajo TX FIFO. But let's just + // check if this FIFO is empty as well. + do { + err = hb_bp_get_tx_fifo_entries(&num_tx_entries, HB_MC_FIFO_TX_REQ); + } while ((num_tx_entries != 0) || (err != HB_MC_SUCCESS)); + + return HB_MC_SUCCESS; +} + +/** + * Signal the hardware to start a bulk transfer over the network + * @param[in] mc A manycore instance initialized with hb_mc_manycore_init() + * @return HB_MC_SUCCESS on success. Otherwise an error code defined in bsg_manycore_errno.h. + */ +int hb_mc_platform_start_bulk_transfer(hb_mc_manycore_t *mc) { + return HB_MC_SUCCESS; +} + +/** + * Signal the hardware to end a bulk transfer over the network + * @param[in] mc A manycore instance initialized with hb_mc_manycore_init() + * @return HB_MC_SUCCESS on success. Otherwise an error code defined in bsg_manycore_errno.h. + */ +int hb_mc_platform_finish_bulk_transfer(hb_mc_manycore_t *mc) { + return HB_MC_SUCCESS; +} + +/** + * Get the current cycle counter of the Manycore Platform + * @param[in] mc A manycore instance initialized with hb_mc_manycore_init() + * @param[out] time The current counter value. + * @return HB_MC_SUCCESS on success. Otherwise an error code defined in bsg_manycore_errno.h. + */ +int hb_mc_platform_get_cycle(hb_mc_manycore_t *mc, uint64_t *time) { + return HB_MC_NOIMPL; +} + +/** + * Get the number of instructions executed for a certain class of instructions + * @param[in] mc A manycore instance initialized with hb_mc_manycore_init() + * @param[in] itype An enum defining the class of instructions to query. + * @param[out] count The number of instructions executed in the queried class. + * @return HB_MC_SUCCESS on success. Otherwise an error code defined in bsg_manycore_errno.h. + */ +int hb_mc_platform_get_icount(hb_mc_manycore_t *mc, bsg_instr_type_e itype, int *count) { + return HB_MC_NOIMPL; +} + +/** + * Enable trace file generation (vanilla_operation_trace.csv) + * @param[in] mc A manycore instance initialized with hb_mc_manycore_init() + * @return HB_MC_SUCCESS on success. Otherwise an error code defined in bsg_manycore_errno.h. + */ +int hb_mc_platform_trace_enable(hb_mc_manycore_t *mc) { + return HB_MC_NOIMPL; +} + +/** + * Disable trace file generation (vanilla_operation_trace.csv) + * @param[in] mc A manycore instance initialized with hb_mc_manycore_init() + * @return HB_MC_SUCCESS on success. Otherwise an error code defined in bsg_manycore_errno.h. + */ +int hb_mc_platform_trace_disable(hb_mc_manycore_t *mc) { + return HB_MC_NOIMPL; +} + +/** + * Enable log file generation (vanilla.log) + * @param[in] mc A manycore instance initialized with hb_mc_manycore_init() + * @return HB_MC_SUCCESS on success. Otherwise an error code defined in bsg_manycore_errno.h. + */ +int hb_mc_platform_log_enable(hb_mc_manycore_t *mc) { + return HB_MC_NOIMPL; +} + +/** + * Disable log file generation (vanilla.log) + * @param[in] mc A manycore instance initialized with hb_mc_manycore_init() + * @return HB_MC_SUCCESS on success. Otherwise an error code defined in bsg_manycore_errno.h. + */ +int hb_mc_platform_log_disable(hb_mc_manycore_t *mc) { + return HB_MC_NOIMPL; +} + +/** + * Block until chip reset has completed. + * @param[in] mc A manycore instance initialized with hb_mc_manycore_init() + * @return HB_MC_SUCCESS on success. Otherwise an error code defined in bsg_manycore_errno.h. + */ +int hb_mc_platform_wait_reset_done(hb_mc_manycore_t *mc) { + hb_mc_packet_t reset_req_pkt, reset_resp_pkt; + int err; + uint32_t data = 0; + + reset_req_pkt.request.x_dst = HOST_X_COORD; + reset_req_pkt.request.y_dst = HOST_Y_COORD; + reset_req_pkt.request.x_src = BP_HOST_LINK_X; + reset_req_pkt.request.y_src = BP_HOST_LINK_Y; + reset_req_pkt.request.op_v2 = HB_MC_PACKET_OP_REMOTE_LOAD; + reset_req_pkt.request.payload = 0; + reset_req_pkt.request.reg_id = 0; + reset_req_pkt.request.addr = HB_BP_HOST_EPA_RESET_DONE; + + do { + // The platform setup ensures that this packet will not go over the network so + // we don't need to check for credits. + err = hb_bp_write_to_mc_bridge(&reset_req_pkt); + if (err != HB_MC_SUCCESS) { + manycore_pr_err(mc, "%s: Write to host request FIFO failed\n", __func__); + return err; + } + + err = hb_bp_read_from_mc_bridge(&reset_resp_pkt, HB_MC_FIFO_RX_RSP); + if (err != HB_MC_SUCCESS) { + manycore_pr_err(mc, "%s: Read from the host response FIFO failed\n", __func__); + return err; + } + + data = reset_resp_pkt.response.data; + } while(data == 0); + + return HB_MC_SUCCESS; +} + diff --git a/libraries/platforms/dromajo-vcs/bsg_manycore_regression_platform.h b/libraries/platforms/dromajo-vcs/bsg_manycore_regression_platform.h new file mode 100644 index 000000000..c8b53a428 --- /dev/null +++ b/libraries/platforms/dromajo-vcs/bsg_manycore_regression_platform.h @@ -0,0 +1,158 @@ +// Copyright (c) 2020, University of Washington All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// Redistributions of source code must retain the above copyright notice, this list +// of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above copyright notice, this +// list of conditions and the following disclaimer in the documentation and/or +// other materials provided with the distribution. +// +// Neither the name of the copyright holder nor the names of its contributors may +// be used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once +#ifdef __cplusplus +extern "C" { +#endif + +// To make your program HammerBlade cross-platform compatible, +// define a function with the signature of "main", and then +// use this macro to mark it as the entry point of your program +// +// Example: +// +// int MyMain(int argc, char *argv[]) { +// /* your code here */ +// } +// declare_program_main("The name of your test", MyMain) +// +#define declare_program_main(test_name, name) \ + int main(int argc, char *argv[]) { \ + bsg_pr_test_info("Regression Test: %s\n", test_name); \ + int rc = name(argc, argv); \ + bsg_pr_test_pass_fail(rc == HB_MC_SUCCESS); \ + return rc; \ + } + +#ifdef __cplusplus +} +#endif + +// Some library functions need to be compiled in both RISC-V +// and x86. Makefile scripts don't recompile a target that was already +// executed even if the file does not exist. Therefore such functions +// are defined as macros here which are included in +// bsg_manycore_simulator.cpp +#define declare_hb_mc_get_bits \ + uint32_t hb_mc_get_bits (uint32_t val, uint32_t start, uint32_t size) { \ + uint32_t mask = ((1 << size) - 1) << start; \ + return ((val & mask) >> start); \ + } \ + +#define declare_bsg_printing \ + typedef struct prefix_info { \ + FILE *file; \ + bool newline; \ + void (*newline_hook)(struct prefix_info *info, const char *prefix); \ +} prefix_info_t; \ + \ +/* inserts the time into the prefix */ \ +static void insert_time(prefix_info_t *info, const char *prefix) \ +{ \ + fprintf(info->file, "%s @ (%llu): ", prefix, bsg_utc()); \ + return; \ +} \ + \ +typedef std::map prefix_map; \ + \ +static prefix_map fmap = { \ + {BSG_PRINT_PREFIX_DEBUG, {BSG_PRINT_STREAM_DEBUG, true, insert_time}}, \ + {BSG_PRINT_PREFIX_ERROR, {BSG_PRINT_STREAM_ERROR, true, 0}}, \ + {BSG_PRINT_PREFIX_WARN, {BSG_PRINT_STREAM_WARN, true, 0}}, \ + {BSG_PRINT_PREFIX_INFO, {BSG_PRINT_STREAM_INFO, true, 0}}, \ +}; \ + \ +int bsg_pr_prefix(const char *prefix, const char *fmt, ...) \ +{ \ + std::string prefix_string(prefix); \ + std::string fmt_string(fmt); \ + prefix_info_t *info; \ + va_list ap; \ + int r = -1, count = 0; \ + bool newline; \ + \ + auto it = fmap.find(prefix_string); \ + if (it != fmap.end()) { \ + info = &it->second; \ + } else { \ + return r; \ + } \ + \ + /* lock our file to make our print atomic */ \ + flockfile(info->file); \ + va_start(ap, fmt); \ + newline = info->newline; \ + \ + std::string::size_type cl_start = 0, cl_end; \ + do { /* for each line in fmt */ \ + /* find end of line */ \ + cl_end = fmt_string.find('\n', cl_start); \ + cl_end = (cl_end == std::string::npos ? fmt_string.size() : cl_end); \ + \ + /* print prefix if this is the start of a new line */ \ + if (newline) { \ + if (info->newline_hook) { \ + info->newline_hook(info, prefix); \ + } else { \ + fprintf(info->file, "%s", prefix); \ + } \ + } \ + \ + /* print to the end of the line */ \ + std::string lfmt = fmt_string.substr(cl_start, cl_end-cl_start); \ + \ + count += vfprintf(info->file, lfmt.c_str(), ap); \ + \ + /* move cl_start forward */ \ + cl_start = cl_end; \ + \ + /* decide on newline */ \ + if (cl_end != fmt_string.size()) { \ + /* print the newline character and set 'newline' to true */ \ + count += fprintf(info->file, "\n"); \ + cl_start++; /* advance one so we don't print the 'newline' twice */ \ + newline = true; \ + } else { \ + /* this was the last line printed */ \ + /* if it was an empty line just set newline to false */ \ + newline = lfmt.empty() ? true : false; \ + } \ + \ + /* until we've reached the end of our format string */ \ + } while (cl_start < fmt_string.size()); \ + \ + /* success */ \ + r = count; \ + \ + exit_func: \ + /* setup for the next call */ \ + info->newline = newline; \ + va_end(ap); \ + funlockfile(info->file); \ + return r; \ +} \ diff --git a/libraries/platforms/dromajo-vcs/bsg_manycore_simulator.cpp b/libraries/platforms/dromajo-vcs/bsg_manycore_simulator.cpp new file mode 100644 index 000000000..5e78f2c63 --- /dev/null +++ b/libraries/platforms/dromajo-vcs/bsg_manycore_simulator.cpp @@ -0,0 +1,706 @@ +// Copyright (c) 2020, University of Washington All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// Redistributions of source code must retain the above copyright notice, this list +// of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above copyright notice, this +// list of conditions and the following disclaimer in the documentation and/or +// other materials provided with the distribution. +// +// Neither the name of the copyright holder nor the names of its contributors may +// be used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// This file implements the SimulatorWrapper object, which hides +// differences in simulators. + +#include +#include +// We use __m128i so that we can pass a 128-bit type between Verilog +// and C. +#include + +// DPI function to advance time +extern "C" { + void bsg_dpi_next(); +} + +////////////////////////////// SimulationWrapper functions ////////////////////////////// + +// Constructor +SimulationWrapper::SimulationWrapper() { + root = new std::string("replicant_tb_top"); + std::string mc_dpi = *root + ".mc_dpi"; + top = svGetScopeFromName(mc_dpi.c_str()); + dpi = new bsg_nonsynth_dpi::dpi_manycore(mc_dpi); + + // Initialize DPI and Dromajo + dromajo_init(); + dpi_init(); + + if (!dromajo) { + bsg_pr_err("%s: Failed to initialize Dromajo pointer\n", __func__); + } + + if (!dpi) { + bsg_pr_err("%s: Failed to initialize DPI pointer\n", __func__); + } + + // Initialize the argument character counter + char_index = 0; +} + +// Destructor +SimulationWrapper::~SimulationWrapper() { + dpi_cleanup(); + dromajo_cosim_fini(dromajo); + this->top = nullptr; +} + +// Sets the arguments +void SimulationWrapper::set_args(int argc, char **argv) { + this->argc = argc; + this->argv = argv; +} + +// Advances time to the next clock edge +void SimulationWrapper::advance_time() { + svScope prev; + prev = svSetScope(top); + bsg_dpi_next(); + svSetScope(prev); +} + +// Advances time by N clock edges +void SimulationWrapper::advance_time_cycles(int N) { + svScope prev; + prev = svSetScope(top); + for (int i = 0; i < N; i++) + bsg_dpi_next(); + svSetScope(prev); +} + +// Does nothing. Turning on/off assertions is only supported in +// Verilator. +void SimulationWrapper::assertOn(bool val){ + +} + +std::string SimulationWrapper::getRoot(){ + return *root; +} + +/* + * dpi_cleanup + * Destroys the pointer to the manycore DPI object + * @params[in] dpi - Pointer to the manycore DPI object + */ +void SimulationWrapper::dpi_cleanup() { + delete dpi; +} + +/* + * dpi_fifo_drain + * Drains all DPI fifos. This function is used during initialization and does not push packets + * to the FIFOs in Dromajo + * @param[in] dpi - Pointer to the manycore DPI object + * @param[in] type - Manycore FIFO type + * @returns success if all the FIFOs were drained correctly + */ +int SimulationWrapper::dpi_fifo_drain(hb_mc_fifo_rx_t type) { + __m128i *pkt; + + int err, drains = 0; + int cap = dpi->config[HB_MC_CONFIG_IO_REMOTE_LOAD_CAP]; + + do { + advance_time(); + + switch(type) { + case HB_MC_FIFO_RX_REQ: + err = dpi->rx_req(*pkt); + break; + case HB_MC_FIFO_RX_RSP: + err = dpi->rx_rsp(*pkt); + break; + default: + bsg_pr_err("%s: Unknown FIFO type\n", __func__); + return HB_MC_FAIL; + } + + if (err == BSG_NONSYNTH_DPI_SUCCESS) + drains++; + } while ((err == BSG_NONSYNTH_DPI_NOT_WINDOW || err == BSG_NONSYNTH_DPI_SUCCESS) + && drains <= cap); + + if (drains == cap) { + bsg_pr_err("%s: Failed to drain FIFO\n", __func__); + return HB_MC_FAIL; + } + + return HB_MC_SUCCESS; +} + +/* + * dpi_init + * Initializes the DPI between the manycore and Dromajo + * @params[in] dpi - Pointer to the manycore DPI object + * @params[in] hierarchy - A C++ string that holds the manycore hierarchy + * @returns success if initialized correctly + */ +int SimulationWrapper::dpi_init() { + advance_time(); + + int err; + + err = dpi_fifo_drain(HB_MC_FIFO_RX_REQ); + if (err != HB_MC_SUCCESS) { + dpi_cleanup(); + return err; + } + + err = dpi_fifo_drain(HB_MC_FIFO_RX_RSP); + if (err != HB_MC_SUCCESS) { + dpi_cleanup(); + return err; + } + + return HB_MC_SUCCESS; +} + +/* + * dromajo_init + * Initializes dromajo with the correct command-line arguments + * @returns a pointer to a dromajo_cosim_state_t object + */ +int SimulationWrapper::dromajo_init() { + char dromajo_str[50]; + char dram_base_str[50]; + char host_str[50]; + char manycore_str[50]; + char prog_str[50]; + + sprintf(dromajo_str, "dromajo"); + sprintf(dram_base_str, "--memory_addr=0x%d", DRAM_BASE_ADDR); + sprintf(host_str, "--host"); + sprintf(manycore_str, "--manycore"); + sprintf(prog_str, "main.elf"); + + char* argv[] = {dromajo_str, dram_base_str, host_str, manycore_str, prog_str}; + dromajo = dromajo_cosim_init(5, argv); + if (!dromajo) { + bsg_pr_err("%s: Failed to initialize Dromajo instance!\n", __func__); + return HB_MC_FAIL; + } + return HB_MC_SUCCESS; +} + +/* + * dromajo_step + * Executes 1 instruction in Dromajo + * @returns true if execution is incomplete and/or without errors + */ +bool SimulationWrapper::dromajo_step() { + int err = dromajo_cosim_step(dromajo, 0, 0, 0, 0, 0, false, false); + return (err != 0) ? false : true; +} + +/* + * dromajo_transmit_host_packet + * Fetches data from the Dromajo->Manycore FIFO and if its a host packet, performs + * the required host operations and pushes responses back on the response FIFO + * @returns success on succesful transmission + */ +int SimulationWrapper::dromajo_transmit_host_packet(hb_mc_packet_t *dromajo_to_host_packet) { + hb_mc_packet_t host_to_dromajo_packet; + int err; + + host_to_dromajo_packet.response.x_dst = dromajo_to_host_packet->request.x_src; + host_to_dromajo_packet.response.y_dst = dromajo_to_host_packet->request.y_src; + host_to_dromajo_packet.response.load_id = 0; + + uint32_t data = 0; + switch (dromajo_to_host_packet->request.op_v2) { + case HB_MC_PACKET_OP_REMOTE_LOAD: + { + // All responses from the host are considered to be of type e_return_int_wb. But a struct similar to + // bsg_manycore_return_packet_type_e is not defined, so return the source packet's op type. Currently, + // the response op type is not used for any operation in the platform, therefore this should be OK. + host_to_dromajo_packet.response.op = dromajo_to_host_packet->request.op_v2; + switch (dromajo_to_host_packet->request.addr) { + case HB_BP_HOST_EPA_ARGS_START ... HB_BP_HOST_EPA_ARGS_FINISH: + { + // Argument indexes for x86 should be byte addressable but the hardware uses word addresses + uint32_t arg_index = ((dromajo_to_host_packet->request.addr - HB_BP_HOST_EPA_ARGS_START) >> 2); + int num_characters = 0; + // If all arguments have been read or there are no arguments to read + // send a finish code + if ((arg_index != this->argc) && (this->argc != 0)) { + // Copy 4 bytes of the arguments + for(int i = 0; i < 4; i++) { + if (this->argv[arg_index][char_index] != '\0') { + data = (data << 8) | ((uint8_t) this->argv[arg_index][char_index]); + num_characters++; + this->char_index++; + } + else { + data = (data << (4 - num_characters) * 8); + this->char_index = 0; + break; + } + } + } + else + data = HB_BP_HOST_OP_FINISH_CODE; + } + break; + case HB_BP_HOST_EPA_CONFIG_START ... HB_BP_HOST_EPA_CONFIG_FINISH: + { + uint32_t idx = ((dromajo_to_host_packet->request.addr - HB_BP_HOST_EPA_CONFIG_START) >> 2); + if (idx <= HB_MC_CONFIG_MAX) + data = dpi->config[idx]; + else { + bsg_pr_err("%s: Configuration ROM index out of bounds\n", __func__); + return HB_MC_NOTFOUND; + } + } + break; + case HB_BP_HOST_EPA_RESET_DONE: + { + bool done; + dpi->reset_is_done(done); + data = done ? 1 : 0; + } + break; + case HB_BP_HOST_EPA_TX_VACANT: + { + bool is_vacant; + dpi->tx_is_vacant(is_vacant); + data = is_vacant ? 1 : 0; + } + break; + default: + { + bsg_pr_err("%s: Invalid address for host load operation\n", __func__); + return HB_MC_FAIL; + } + + } + host_to_dromajo_packet.response.data = data; + for(int j = 0; j < 4; j++) { + mc_to_host_resp_fifo->fifo[j].push(host_to_dromajo_packet.words[j]); + host_to_mc_req_fifo->fifo[j].pop(); + } + } + break; + case HB_MC_PACKET_OP_REMOTE_SW: + { + switch (dromajo_to_host_packet->request.addr) { + case HB_MC_HOST_EPA_STDOUT: + case HB_MC_HOST_EPA_STDERR: + { + printf("%c", (uint8_t) dromajo_to_host_packet->request.payload); + fflush(stdout); + } + break; + case HB_MC_HOST_EPA_FINISH: + { + int16_t core_id = dromajo_to_host_packet->request.payload >> 16; + // Success error codes in BP is 0, but 0 is already used by the manycore. Any positive number indicates + // success, therefore, add 1. + int err = (0x0000FFFF & dromajo_to_host_packet->request.payload) + 1; + bsg_pr_info("Core %d successfully terminated\n", core_id); + // De-allocate all pointers prior to termination + dpi_cleanup(); + dromajo_cosim_fini(dromajo); + return err; + } + break; + case HB_MC_HOST_EPA_FAIL: + { + int16_t core_id = dromajo_to_host_packet->request.payload >> 16; + int16_t err = 0x0000FFFF & dromajo_to_host_packet->request.payload; + bsg_pr_err("Core %d terminated with code %d\n", core_id, err); + // De-allocate all pointers prior to termination + dpi_cleanup(); + dromajo_cosim_fini(dromajo); + return err; + } + break; + default: + { + bsg_pr_err("%s: Invalid address for host store operation\n", __func__); + return HB_MC_FAIL; + } + } + // Store requests don't have responses so only pop the host to MC fifo + for(int j = 0; j < 4; j++) { + host_to_mc_req_fifo->fifo[j].pop(); + } + } + break; + default: + { + bsg_pr_err("%s: Operations other than loads and store words are not implemented for the host\n", __func__); + return HB_MC_FAIL; + } + break; + } + + return HB_MC_SUCCESS; +} + +/* + * dromajo_transmit_device_packet + * Fetches data from the Dromajo->Manycore FIFO and if its a device packet, forwards + * it to the Manycore DPI TX FIFO + * @returns success on succesful transmission + */ +int SimulationWrapper::dromajo_transmit_device_packet(hb_mc_packet_t *dromajo_to_device_packet) { + __m128i *pkt; + int err; + pkt = reinterpret_cast<__m128i *>(dromajo_to_device_packet); + + // Allows the DPI interface to track response FIFO capacity + bool expect_response = + (dromajo_to_device_packet->request.op_v2 != HB_MC_PACKET_OP_REMOTE_STORE) && + (dromajo_to_device_packet->request.op_v2 != HB_MC_PACKET_OP_REMOTE_SW) && + (dromajo_to_device_packet->request.op_v2 != HB_MC_PACKET_OP_CACHE_OP); + + // Attempt packet transmission + // Since we trigger a call to the transmit FIFOs only when all the 32-bit Dromajo + // FIFOs are full (i.e. a single 128-bit packet is ready), we need to wait until + // the DPI FIFOs are ready to receive before advancing to the next operation. + // This can prevent filling up of the FIFOs. However, not doing this can help in + // identifying situations that might create backpressure in actual hardware and + // provision for it. + do { + advance_time(); + err = dpi->tx_req(*pkt, expect_response); + } while (err != BSG_NONSYNTH_DPI_SUCCESS && + (err == BSG_NONSYNTH_DPI_NO_CREDITS || + err == BSG_NONSYNTH_DPI_NO_CAPACITY || + err == BSG_NONSYNTH_DPI_NOT_WINDOW || + err == BSG_NONSYNTH_DPI_BUSY || + err == BSG_NONSYNTH_DPI_NOT_READY)); + + if (err == BSG_NONSYNTH_DPI_SUCCESS) { + for (int i = 0;i < 4; i++) + host_to_mc_req_fifo->fifo[i].pop(); + } + else { + bsg_pr_err("%s: Packet transmission failed\n", __func__); + return HB_MC_FAIL; + } + + return HB_MC_SUCCESS; +} + +/* + * dromajo_transmit_packet + * Fetches data from the Dromajo->Manycore FIFO and pushes it into the DPI FIFO + * to send packets to the manycore or the host + * @returns success on succesful transmission + */ +int SimulationWrapper::dromajo_transmit_packet() { + hb_mc_packet_t dromajo_to_mc_packet; + int err; + __m128i *pkt; + + mc_fifo_type_t type = FIFO_HOST_TO_MC_REQ; + bool is_empty; + + do { + // Read the FIFO head pointer for all 32-bit FIFOs + dromajo_to_mc_packet.words[0] = host_to_mc_req_fifo->fifo[0].front(); + dromajo_to_mc_packet.words[1] = host_to_mc_req_fifo->fifo[1].front(); + dromajo_to_mc_packet.words[2] = host_to_mc_req_fifo->fifo[2].front(); + dromajo_to_mc_packet.words[3] = host_to_mc_req_fifo->fifo[3].front(); + + // Intercept packets that are for the host and generate appropriate responses + // TODO: Currently, these packets don't go over the network. In the real system (with BP), some or all of these may be required to go + // over the network. The platform implementation assumes the host packets do not go over the network. In some cases (like fences) this + // might not be desirable. Make sure to keep the platform and the system host in sync on what is actually being simulated. + if ((dromajo_to_mc_packet.request.x_dst == HOST_X_COORD) && (dromajo_to_mc_packet.request.y_dst == HOST_Y_COORD)) { + err = dromajo_transmit_host_packet(&dromajo_to_mc_packet); + // This serves to return the error from the host function + // It is also used to communicate the success code for successful program termination but this code is + // not HB_MC_SUCCESS (maps to 0) but a positive number + if (err != HB_MC_SUCCESS) + return err; + } + else { + err = dromajo_transmit_device_packet(&dromajo_to_mc_packet); + if (err != HB_MC_SUCCESS) + return err; + } + is_empty = mc_is_fifo_empty(type); + } while (!is_empty); + + return HB_MC_SUCCESS; +} + +/* + * dromajo_receive_packet + * Receives packets from the DPI FIFOs and pushes the data into the + * manycore request and response FIFOs in Dromajo + */ +int SimulationWrapper::dromajo_receive_packet() { + hb_mc_packet_t *mc_to_dromajo_req_packet, *mc_to_dromajo_resp_packet; + int err; + __m128i pkt; + + // Read from the manycore request FIFO + // At every clock edge we are polling the request FIFO to see if there + // is a packet. If there is no packet (i.e err == BSG_NONSYNTH_NOT_VALID) + // we must move on + do { + do { + advance_time(); + err = dpi->rx_req(pkt); + } while (err != BSG_NONSYNTH_DPI_SUCCESS && + (err == BSG_NONSYNTH_DPI_NOT_WINDOW || + err == BSG_NONSYNTH_DPI_BUSY)); + + if (err == BSG_NONSYNTH_DPI_SUCCESS) { + mc_to_dromajo_req_packet = reinterpret_cast(&pkt); + for (int i = 0; i < 4; i++) { + mc_to_host_req_fifo->fifo[i].push(mc_to_dromajo_req_packet->words[i]); + } + } + } while (err != BSG_NONSYNTH_DPI_NOT_VALID); + + // Read from the manycore response FIFO + do { + do { + advance_time(); + err = dpi->rx_rsp(pkt); + } while (err != BSG_NONSYNTH_DPI_SUCCESS && + (err == BSG_NONSYNTH_DPI_NOT_WINDOW || + err == BSG_NONSYNTH_DPI_BUSY)); + + if (err == BSG_NONSYNTH_DPI_SUCCESS) { + mc_to_dromajo_resp_packet = reinterpret_cast(&pkt); + for (int i = 0; i < 4; i++) { + mc_to_host_resp_fifo->fifo[i].push(mc_to_dromajo_resp_packet->words[i]); + } + } + } while (err != BSG_NONSYNTH_DPI_NOT_VALID); + + return HB_MC_SUCCESS; +} + +/* + * dromajo_update_credits + * Polls the hardware for credit information and updates the credits info + * for the Dromajo->Manycore request FIFO in dromajo + */ +int SimulationWrapper::dromajo_update_credits() { + int credits_used; + int err; + + err = dpi->get_credits_used(credits_used); + if (err != BSG_NONSYNTH_DPI_SUCCESS) { + bsg_pr_err(bsg_nonsynth_dpi_strerror(err)); + return HB_MC_FAIL; + } + + if (credits_used < 0) { + bsg_pr_err("%s: Credit value is < 0. Must be non-negative\n", __func__); + return err; + } + host_to_mc_req_fifo->credits = credits_used; + + return HB_MC_SUCCESS; +} + +int SimulationWrapper::eval(){ + bool transmit = false; + int err; + + // Execute NUM_DROMAJO_INSTR_PER_TICK instructions on Dromajo + for(int i = 0; i < NUM_DROMAJO_INSTR_PER_TICK; i++) { + if (!dromajo_step()) { + // Fixme: Dromajo could also terminate due to premature errors. + // Use the test outputs to detect PASS/FAIL + bsg_pr_info("Dromajo Execution Complete!\nExiting...\n"); + dromajo_cosim_fini(dromajo); + return HB_MC_SUCCESS; + } + + // Advancing time is required for things to move around in hardware, however + // it adds a large overhead to the simulation time. + advance_time();; + + // Update the credits in dromajo + // This might be a costly operation to do on every eval call + // because it is a DPI call, however it is required to update the + // credits for cases like in fences. + err = dromajo_update_credits(); + if (err != HB_MC_SUCCESS) + return err; + + // Check if transmit FIFO has an element and hence ready to transmit + // Every NUM_TX_FIFO_CHK_PER_TICK iterations or if there is an element + // in the TX FIFO, drain it immediately. + mc_fifo_type_t type = FIFO_HOST_TO_MC_REQ; + bool is_empty = mc_is_fifo_empty(type); + if ((i % NUM_TX_FIFO_CHK_PER_TICK == 0) || !is_empty) { + if (!is_empty) { + transmit = true; + break; + } + } + } + + if (transmit) { + // Poll for packets to be transmitted and transmit them + bsg_pr_dbg("Checking for packets to transmit\n"); + err = dromajo_transmit_packet(); + if (err != HB_MC_SUCCESS) + return err; + } + + // Poll for packets to be received and push them to Dromajo + // This involves a DPI call and might incur a large overhead, therefore + // check for packets to receive only once per tick + bsg_pr_dbg("Checking for packets to receive\n"); + err = dromajo_receive_packet(); + if (err != HB_MC_SUCCESS) + return err; + + return HB_MC_SUCCESS; +} + +////////////////////////////// Interacting with VCS ////////////////////////////// + +/* + * get_argc + * Given a string, determine the number of space-separated arguments + * @params[in] args - Pointer to a character array that holds the arguments + * @returns the number of arguments + */ +static int get_argc(char * args){ + char *cur = args, prev=' '; + int count = 1; + while(*cur != '\0'){ + if((prev == ' ') && (prev != *cur)){ + count ++; + } + prev = *cur; + ++cur; + } + return count; +} + +/* + * get_argv + * Given a string, retrieves the space-separated arguments + * @params[in] args - Pointer to a character array that holds the arguments + * @params[in] argc - Number of arguments + * @params[in] argv - Pointer to an array of strings that will hold the different + * arguments + */ +static void get_argv(char * args, int argc, char **argv){ + int count = 0; + char *cur = args, prev=' '; + + // First parse the path name. This is not in the argument string because + // VCS doesn't provide it to us. Instead, we "hack" around it by reading + // the path from 'proc/self/exe'. The maximum path-name length is 1024, + // with an extra null character for safety + static char path[1025] = {'\0'}; + + readlink("/proc/self/exe", path, sizeof(path) - 1); + argv[0] = path; + count ++; + + // Then we parse the remaining arguments. Arguments are separated by N + // >= 1 spaces. We only register an argument when the previous character + // was a space, and the current character is not (so that multiple + // spaces don't count as multiple arguments). We replace spaces with + // null characters (\0) so that each argument appears to be an + // individual string and can be used later, by argparse (and other + // libraries) + while(*cur != '\0'){ + if((prev == ' ') && (prev != *cur)){ + argv[count] = cur; + count++; + } + prev = *cur; + if(*cur == ' ') + *cur = '\0'; + cur++; + } +} + +/* + * vcs_main + * The main function for the x86 host. It initializes all devices and controls + * the time in simulation. It handles all DPI calls and passes packets to and from + * Dromajo during execution + * @params[in] argc - Number of arguments + * @params[in] argv - Pointer to an array of string arguments + * @returns + */ +int vcs_main(int argc, char **argv) { + // Initialize Host + SimulationWrapper *host = new SimulationWrapper(); + if (!host) { + bsg_pr_err("%s: Could not initialize host!\n", __func__); + return HB_MC_FAIL; + } + + // Assign arguments + host->set_args(argc, argv); + + int err; + // TODO: Currently the simulation terminates by encoding the error code and the core + // ID in the 32-bit data field of a finish packet sent to the host. Need to come up + // with a more elegant solution for this. + do { + err = host->eval(); + // Codes greater than 0 can be used to terminate a program + if (err > 0) break; + } while (err >= 0); + + return err; +} + +/* + * cosim_main + * This function is the VCS hook for cosimulation + * @params[in] exit_code - A pointer to an integer that holds the exit code + * @params[in] args - A character array that holds the space-separated + * arguments to the function + */ +extern "C" { + void cosim_main(uint32_t *exit_code, char *args) { + // We aren't passed command line arguments directly so we parse them + // from *args. args is a string from VCS - to pass a string of arguments + // to args, pass c_args to VCS as follows: +c_args="" + int argc = get_argc(args); + char *argv[argc]; + get_argv(args, argc, argv); + + int rc = vcs_main(argc, argv); + *exit_code = rc; + bsg_pr_test_pass_fail(rc >= HB_MC_SUCCESS); + return; + } +} diff --git a/libraries/platforms/dromajo-vcs/bsg_manycore_simulator.hpp b/libraries/platforms/dromajo-vcs/bsg_manycore_simulator.hpp new file mode 100644 index 000000000..e9be9e1f6 --- /dev/null +++ b/libraries/platforms/dromajo-vcs/bsg_manycore_simulator.hpp @@ -0,0 +1,132 @@ +// Copyright (c) 2020, University of Washington All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// Redistributions of source code must retain the above copyright notice, this list +// of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above copyright notice, this +// list of conditions and the following disclaimer in the documentation and/or +// other materials provided with the distribution. +// +// Neither the name of the copyright holder nor the names of its contributors may +// be used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// This file implements the SimulationWrapper object. + +#ifndef __BSG_MANYCORE_SIMULATOR_HPP +#define __BSG_MANYCORE_SIMULATOR_HPP + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include + +// Define invisible library functions +declare_hb_mc_get_bits +declare_bsg_printing + +#ifndef DRAM_BASE_ADDR +#error BlackParrot DRAM base address is undefined +#endif + +#ifndef NUM_DROMAJO_INSTR_PER_TICK +#define NUM_DROMAJO_INSTR_PER_TICK 1000 +#endif + +#ifndef NUM_TX_FIFO_CHK_PER_TICK +#define NUM_TX_FIFO_CHK_PER_TICK 100 +#endif + +class SimulationWrapper{ + // This is the generic pointer for implementation-specific + // simulator details. In Verilator, this is + // Vmanycore_tb_top. In VCS this is the scope + // for DPI. + void *top = nullptr; + std::string *root; + // Pointer to the dromajo instance + dromajo_cosim_state_t *dromajo; + // Pointer to the manycore DPI controller + bsg_nonsynth_dpi::dpi_manycore *dpi; + // Fields to keep track of arguments + int argc; + char **argv; + int char_index; + +public: + SimulationWrapper(); + ~SimulationWrapper(); + + // Set the argument fields to pass them into Dromajo + void set_args(int argc, char **argv); + + // Change the assertion state. + + // When Verilator simulation starts, we want to disable + // assertion because it is a two-state simulator and the lack + // of z/x may cause erroneous assertions. + // + // This does not need to be implemented in 4-state simulators + // like VCS + void assertOn(bool val); + + std::string getRoot(); + + // Advances to the next clock edge + void advance_time(); + // Advances time by N clock edges + void advance_time_cycles(int N); + + // DPI functions + int dpi_fifo_drain(hb_mc_fifo_rx_t type); + void dpi_cleanup(); + int dpi_init(); + + // Dromajo functions + int dromajo_init(); + bool dromajo_step(); + int dromajo_transmit_packet(); + int dromajo_transmit_host_packet(hb_mc_packet_t *dromajo_to_host_packet); + int dromajo_transmit_device_packet(hb_mc_packet_t *dromajo_to_device_packet); + int dromajo_receive_packet(); + int dromajo_update_credits(); + + // Evaluates a maximum of NUM_DROMAJO_INSTR_PER_TICK instructions + // in RISC-V and forwards packets between Dromajo and the manycore + int eval(); +}; +#endif // __BSG_MANYCORE_SIMULATOR_HPP diff --git a/libraries/platforms/dromajo-vcs/compilation.mk b/libraries/platforms/dromajo-vcs/compilation.mk new file mode 100644 index 000000000..791d5119e --- /dev/null +++ b/libraries/platforms/dromajo-vcs/compilation.mk @@ -0,0 +1,78 @@ +# Copyright (c) 2020, University of Washington All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# Redistributions of source code must retain the above copyright notice, this list +# of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above copyright notice, this +# list of conditions and the following disclaimer in the documentation and/or +# other materials provided with the distribution. +# +# Neither the name of the copyright holder nor the names of its contributors may +# be used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# This Makefile fragment defines rules for compilation of the C/C++ +# files for running regression tests. + +ORANGE=\033[0;33m +RED=\033[0;31m +NC=\033[0m + +# Include the machine configuration to make things clear(er) +include $(BSG_MACHINE_PATH)/Makefile.machine.include + +# This file REQUIRES several variables to be set. They are typically +# set by the Makefile that includes this makefile.. +# +DEFINES += -D_DRAMFS -DHOST_X_COORD=$(BSG_MACHINE_HOST_COORD_X) -DHOST_Y_COORD=$(BSG_MACHINE_HOST_COORD_Y) -DBP_POD_X=0 -DBP_POD_Y=1 +INCLUDES += -I$(LIBRARIES_PATH) +INCLUDES += -I$(BSG_PLATFORM_PATH) +INCLUDES += -I$(BSG_PLATFORM_PATH)/software/include +INCLUDES += -I$(BLACKPARROT_SDK_DIR)/perch + +CXXFLAGS += $(DEFINES) -march=rv64imafd -mabi=lp64 -mcmodel=medany +CFLAGS += $(DEFINES) -march=rv64imafd -mabi=lp64 -mcmodel=medany +ASFLAGS += $(DEFINES) -march=rv64imafd -mabi=lp64 -mcmodel=medany + +# Default to RISC-V but overridden by the flow to other compilers as needed +CC ?= $(BLACKPARROT_SDK_DIR)/install/bin/riscv64-unknown-elf-dramfs-gcc +CXX ?= $(BLACKPARROT_SDK_DIR)/install/bin/riscv64-unknown-elf-dramfs-g++ +OBJDUMP ?= $(BLACKPARROT_SDK_DIR)/install/bin/riscv64-unknown-elf-dramfs-objdump + +# each regression target needs to build its .o from a .c and .h of the +# same name +%.o: %.c + $(CC) -c -o $@ $< $(INCLUDES) $(CFLAGS) $(CDEFINES) + +# ... or a .cpp and .hpp of the same name +%.o: %.cpp + $(CXX) -c -o $@ $< $(INCLUDES) $(CXXFLAGS) $(CXXDEFINES) + +%.o: %.S + $(CC) -c -o $@ $< $(INCLUDES) $(ASFLAGS) + +%.dis: %.elf + $(OBJDUMP) -d -t $^ > $@ + +.PRECIOUS: %.o + +.PHONY: platform.compilation.clean +platform.compilation.clean: + rm -rf *.o + rm -rf *.dis + +compilation.clean: platform.compilation.clean diff --git a/libraries/platforms/dromajo-vcs/execution.mk b/libraries/platforms/dromajo-vcs/execution.mk new file mode 100644 index 000000000..a1d38794f --- /dev/null +++ b/libraries/platforms/dromajo-vcs/execution.mk @@ -0,0 +1,74 @@ +# Copyright (c) 2020, University of Washington All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# Redistributions of source code must retain the above copyright notice, this list +# of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above copyright notice, this +# list of conditions and the following disclaimer in the documentation and/or +# other materials provided with the distribution. +# +# Neither the name of the copyright holder nor the names of its contributors may +# be used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +# All simulations should run with +ntb_random_seed_automatic. +# +# From the VCS MX User-Guide: +ntb_random_seed_automatic Picks a unique value to +# supply as the first seed used by a testbench. The value is determined by +# combining the time of day, host name and process id. This ensures that no two +# simulations have the same starting seed. +SIM_ARGS += +ntb_random_seed_automatic + +# These are the execution rules for the binaries. We can't pass +# C-style arguments through the command line, so instead we specify +# them as the VCS plusarg argument +c_args. Users can specify C-style +# arguments using the C_ARGS make variable. + +# Compile the manycore binary before building the rest of the library so that we can +# add the entire manycore binary somewhere for Dromajo/BlackParrot to load into the +# DRAM. This is only a patch until we can do it the right way i.e. use a filesystem +%.log: $(BSG_MANYCORE_KERNELS) % + ./$* $(SIM_ARGS) +c_args="$(C_ARGS)" 2>&1 | tee $@ + +vanilla_stats.csv vcache_stats.csv router_stat.csv: % : %.profile.log + +%.saif: %.saifgen.log ; + +%.vpd: SIM_ARGS += +vpdfile+$(@:.debug.log=.vpd) +%.vpd: %.debug.log ; + +%.dve: %.vpd + $(DVE) -full64 -vpd $< & + +.PRECIOUS: %.log + +.PHONY: platform.execution.clean %.log %.vpd +platform.execution.clean: + rm -rf vanilla_stats.csv + rm -rf infinite_mem_stats.csv + rm -rf vcache_stats.csv + rm -rf vanilla_operation_trace.csv + rm -rf operation_trace.csv + rm -rf vcache_operation_trace.csv + rm -rf router_stat.csv + rm -rf remote_load_trace.csv + rm -rf vanilla.log + rm -rf *.vpd + rm -rf dramsim3.json dramsim3.tag.json dramsim3.txt dramsim3epoch.json + +execution.clean: platform.execution.clean diff --git a/libraries/platforms/dromajo-vcs/hardware.mk b/libraries/platforms/dromajo-vcs/hardware.mk new file mode 100644 index 000000000..004613fe0 --- /dev/null +++ b/libraries/platforms/dromajo-vcs/hardware.mk @@ -0,0 +1,155 @@ +# Copyright (c) 2020, University of Washington All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# Redistributions of source code must retain the above copyright notice, this list +# of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above copyright notice, this +# list of conditions and the following disclaimer in the documentation and/or +# other materials provided with the distribution. +# +# Neither the name of the copyright holder nor the names of its contributors may +# be used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# hardware.mk: Platform-specific HDL listing. +# +# For simulation platforms, it also describes how to build the +# simulation "libraries" that are required by CAD tools. +# +# This file should be included from bsg_replicant/hardware/hardware.mk. It checks +# BSG_PLATFORM_PATH, BASEJUMP_STL_DIR, BSG_MANYCORE_DIR, etc. + +# BSG_MACHINE_NAME: The name of the target machine. Should be defined +# in $(BSG_MACHINE_PATH)/Makefile.machine.include, which is included +# by hardware.mk +ifndef BSG_MACHINE_NAME +$(error $(shell echo -e "$(RED)BSG MAKE ERROR: BSG_MACHINE_NAME is not defined$(NC)")) +endif + +# The following variables are set by $(BSG_F1_DIR)/hdk.mk, which will fail if +# hdk_setup.sh has not been run, or environment.mk is not included +# +# HDK_SHELL_DESIGN_DIR: Path to the directory containing all the AWS "shell" IP +# AWS_FPGA_REPO_DIR: Path to the clone of the aws-fpga repo +# HDK_COMMON_DIR: Path to HDK 'common' directory w/ libraries for cosimluation. +# SDK_DIR: Path to the SDK directory in the aws-fpga repo +include $(BSG_F1_DIR)/hdk.mk + +################################################################################ +# Simulation Sources +################################################################################ +VSOURCES += $(BASEJUMP_STL_DIR)/bsg_test/bsg_nonsynth_clock_gen.v +VSOURCES += $(BASEJUMP_STL_DIR)/bsg_test/bsg_nonsynth_reset_gen.v + +POD_TRACE_GEN_PY = $(BSG_MANYCORE_DIR)/testbenches/py/pod_trace_gen.py +$(BSG_MACHINE_PATH)/bsg_tag_boot_rom.tr: $(BSG_MACHINE_PATH)/Makefile.machine.include + env python2 $(POD_TRACE_GEN_PY) $(BSG_MACHINE_PODS_X) $(BSG_MACHINE_PODS_Y) $(BSG_MACHINE_NOC_COORD_X_WIDTH) > $@ + +ASCII_TO_ROM_PY = $(BASEJUMP_STL_DIR)/bsg_mem/bsg_ascii_to_rom.py +$(BSG_MACHINE_PATH)/bsg_tag_boot_rom.v: $(BSG_MACHINE_PATH)/bsg_tag_boot_rom.tr + env python2 $(ASCII_TO_ROM_PY) $< bsg_tag_boot_rom > $@ + +VSOURCES += $(BSG_MACHINE_PATH)/bsg_tag_boot_rom.v +VSOURCES += $(BASEJUMP_STL_DIR)/bsg_fsb/bsg_fsb_node_trace_replay.v +VSOURCES += $(BASEJUMP_STL_DIR)/bsg_tag/bsg_tag_trace_replay.v +VSOURCES += $(BASEJUMP_STL_DIR)/bsg_tag/bsg_tag_master.v + +# DMA Interface +VSOURCES += $(BSG_MANYCORE_DIR)/testbenches/common/v/vcache_dma_to_dram_channel_map.v +VSOURCES += $(BASEJUMP_STL_DIR)/bsg_mem/bsg_nonsynth_mem_1r1w_sync_dma.v +VSOURCES += $(BASEJUMP_STL_DIR)/bsg_mem/bsg_nonsynth_mem_1r1w_sync_mask_write_byte_dma.v +VSOURCES += $(BASEJUMP_STL_DIR)/bsg_mem/bsg_nonsynth_mem_1rw_sync_mask_write_byte_dma.v + +# DRAMSim3 +VSOURCES += $(BASEJUMP_STL_DIR)/bsg_test/bsg_dramsim3_pkg.v +VSOURCES += $(BASEJUMP_STL_DIR)/bsg_test/bsg_nonsynth_dramsim3.v +VSOURCES += $(BASEJUMP_STL_DIR)/bsg_test/bsg_nonsynth_dramsim3_map.v +VSOURCES += $(BASEJUMP_STL_DIR)/bsg_test/bsg_nonsynth_dramsim3_unmap.v + +VSOURCES += $(BASEJUMP_STL_DIR)/bsg_cache/bsg_cache_to_test_dram.v +VSOURCES += $(BASEJUMP_STL_DIR)/bsg_cache/bsg_cache_to_test_dram_tx.v +VSOURCES += $(BASEJUMP_STL_DIR)/bsg_cache/bsg_cache_to_test_dram_rx.v +VSOURCES += $(BASEJUMP_STL_DIR)/bsg_cache/bsg_cache_to_test_dram_rx_reorder.v + +# Infinite Memory +VSOURCES += $(BSG_MANYCORE_DIR)/testbenches/common/v/bsg_nonsynth_mem_infinite.v + +# Profiling +VSOURCES += $(BSG_MANYCORE_DIR)/testbenches/common/v/bsg_manycore_profile_pkg.v +VSOURCES += $(BASEJUMP_STL_DIR)/bsg_misc/bsg_cycle_counter.v + +# Core Profiler/Trace +VSOURCES += $(BASEJUMP_STL_DIR)/bsg_test/bsg_nonsynth_dpi_gpio.v +VSOURCES += $(BSG_MANYCORE_DIR)/testbenches/common/v/instr_trace.v +VSOURCES += $(BSG_MANYCORE_DIR)/testbenches/common/v/vanilla_core_trace.v +VSOURCES += $(BSG_MANYCORE_DIR)/testbenches/common/v/vanilla_core_profiler.v +# We reuse the print_stat_snoop module from aws-vcs +VSOURCES += $(LIBRARIES_PATH)/platforms/aws-vcs/hardware/bsg_print_stat_snoop.v + +VSOURCES += $(BSG_MANYCORE_DIR)/testbenches/common/v/router_profiler.v +VSOURCES += $(BSG_MANYCORE_DIR)/testbenches/common/v/remote_load_trace.v + +# Memory Profilers +VSOURCES += $(BSG_MANYCORE_DIR)/testbenches/common/v/vcache_profiler.v +VSOURCES += $(BSG_MANYCORE_DIR)/testbenches/common/v/vcache_non_blocking_profiler.v +VSOURCES += $(BSG_MANYCORE_DIR)/testbenches/common/v/infinite_mem_profiler.v + +VSOURCES += $(BSG_MANYCORE_DIR)/testbenches/common/v/bsg_nonsynth_manycore_tag_master.v +VSOURCES += $(BSG_MANYCORE_DIR)/testbenches/common/v/bsg_nonsynth_manycore_io_complex.v +VSOURCES += $(BSG_MANYCORE_DIR)/testbenches/common/v/bsg_nonsynth_manycore_spmd_loader.v +VSOURCES += $(BSG_MANYCORE_DIR)/testbenches/common/v/bsg_nonsynth_manycore_monitor.v +VSOURCES += $(BSG_MANYCORE_DIR)/testbenches/common/v/bsg_nonsynth_wormhole_test_mem.v +VSOURCES += $(BSG_MANYCORE_DIR)/testbenches/common/v/bsg_manycore_vcache_wh_to_cache_dma.v +VSOURCES += $(BSG_MANYCORE_DIR)/testbenches/common/v/bsg_nonsynth_manycore_testbench.v + +VSOURCES += $(BASEJUMP_STL_DIR)/bsg_dataflow/bsg_serial_in_parallel_out_full.v +VSOURCES += $(BASEJUMP_STL_DIR)/bsg_dataflow/bsg_round_robin_1_to_n.v +VSOURCES += $(BASEJUMP_STL_DIR)/bsg_dataflow/bsg_one_fifo.v + +VSOURCES += $(HARDWARE_PATH)/bsg_manycore_endpoint_to_fifos_pkg.v +VSOURCES += $(HARDWARE_PATH)/bsg_manycore_endpoint_to_fifos.v + +VSOURCES += $(BSG_MANYCORE_DIR)/testbenches/common/v/vanilla_core_saif_dumper.v + +################################################################################ +# DPI-Specific Sources +################################################################################ + +VSOURCES += $(BSG_MANYCORE_DIR)/v/bsg_manycore_link_sif_async_buffer.v +VSOURCES += $(BASEJUMP_STL_DIR)/bsg_test/bsg_nonsynth_dpi_from_fifo.v +VSOURCES += $(BASEJUMP_STL_DIR)/bsg_test/bsg_nonsynth_dpi_to_fifo.v +VSOURCES += $(BASEJUMP_STL_DIR)/bsg_test/bsg_nonsynth_dpi_rom.v +VSOURCES += $(BSG_MANYCORE_DIR)/testbenches/dpi/bsg_nonsynth_dpi_manycore.v + +VSOURCES += $(BASEJUMP_STL_DIR)/bsg_test/bsg_nonsynth_dpi_cycle_counter.v + +################################################################################ +# Top-level files +################################################################################ +# Top-level module name +BSG_DESIGN_TOP := replicant_tb_top + +VSOURCES += $(LIBRARIES_PATH)/platforms/bigblade-vcs/hardware/dpi_top.sv + +VINCLUDES += $(BSG_PLATFORM_PATH)/hardware +VINCLUDES += $(BSG_PLATFORM_PATH) + + +hardware.clean: machine.hardware.clean + +machine.hardware.clean: + rm -rf $(BSG_MACHINE_PATH)/bsg_tag_boot_rom.tr $(BSG_MACHINE_PATH)/bsg_tag_boot_rom.v diff --git a/libraries/platforms/dromajo-vcs/hb_bp_platform.h b/libraries/platforms/dromajo-vcs/hb_bp_platform.h new file mode 100644 index 000000000..de336b117 --- /dev/null +++ b/libraries/platforms/dromajo-vcs/hb_bp_platform.h @@ -0,0 +1,135 @@ +// Copyright (c) 2020, University of Washington All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// Redistributions of source code must retain the above copyright notice, this list +// of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above copyright notice, this +// list of conditions and the following disclaimer in the documentation and/or +// other materials provided with the distribution. +// +// Neither the name of the copyright holder nor the names of its contributors may +// be used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// BlackParrot platform-specific header file +// This file contains manycore-specific platform functions and constants + +#ifndef HB_BP_PLATFORM_H +#define HB_BP_PLATFORM_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +// Host X and Y coordinates +#ifndef HOST_X_COORD +#error HOST_X_COORD is undefined +#endif + +#ifndef HOST_Y_COORD +#error HOST_Y_COORD is undefined +#endif + +// Coordinates of BP-HB bridge used for manycore communication +#define BP_HOST_LINK_X 15 +#define BP_HOST_LINK_Y 9 +// Coordinates of BP-HB bridge used for DRAM loads and stores +#define BP_DRAM_0_LINK_X 15 +#define BP_DRAM_0_LINK_Y 10 +#define BP_DRAM_1_LINK_X 15 +#define BP_DRAM_1_LINK_Y 11 + +// BlackParrot pod coordinates +#ifndef BP_POD_X +#error BP_POD_X is not defined +#endif + +#ifndef BP_POD_Y +#error BP_POD_Y is not defined +#endif + +// Memory mapped addresses to interact with the BlackParrot host (aka simulator) +#define HB_BP_HOST_EPA_ARGS_START 0x0000 +#define HB_BP_HOST_EPA_ARGS_FINISH 0x00FF +#define HB_BP_HOST_EPA_CONFIG_START 0x0100 +#define HB_BP_HOST_EPA_CONFIG_FINISH 0x01FF +#define HB_BP_HOST_EPA_RESET_DONE 0x0200 +#define HB_BP_HOST_EPA_TX_VACANT 0x0300 + +#define HB_BP_HOST_OP_FINISH_CODE 0xFFFFFFFF +#define DROMAJO_RW_FAIL_CODE 0xFFFFFFFF + +// Memory mapped addresses to interact with the manycore bridge +// BlackParrot currently sets the MSB of its address to 1 to commnicate with the manycore +// Dromajo however is setup to identify the manycore as a device +#define MC_BASE_ADDR 0x500000 +#define BP_TO_MC_REQ_FIFO_ADDR 0x1000 +#define BP_TO_MC_REQ_CREDITS_ADDR 0x2000 +#define BP_TO_MC_REQ_ENTRIES_ADDR 0x3000 +#define MC_TO_BP_RESP_FIFO_ADDR 0x4000 +#define MC_TO_BP_RESP_ENTRIES_ADDR 0x5000 +#define MC_TO_BP_REQ_FIFO_ADDR 0x6000 +#define MC_TO_BP_REQ_ENTRIES_ADDR 0x7000 + +/**************************** Manycore Bridge helper functions ****************************/ +// Use these functions to enable Dromajo/BlackParrot to write to the bridge. These functions +// are undefined and carry no meaning when used by the BlackParrot host (aka simulator) + +/* + * Reads the manycore bridge for number of credits used in the endpoint + * @returns number of credits in the manycore bridge enddpoint + */ +int hb_bp_get_credits_used(int *credits_used); + +/* + * Writes a 128-bit manycore packet in 32-bit chunks to the manycore bridge FIFO + * @param[in] pkt --> Pointer to the manycore packet + */ +int hb_bp_write_to_mc_bridge(hb_mc_packet_t *pkt); + +/* + * Checks if the MC to BP FIFO contains any entries + * @param[in] entries --> Pointer to a location in memory that will hold the number of entries + * @param[in] type --> Type of FIFO to poll for entries + * @returns number of entries in the MC to BP FIFO + */ +int hb_bp_get_rx_fifo_entries(int *entries, hb_mc_fifo_rx_t type); + +/* + * Checks if the BP to MC FIFO contains any entries + * @param[in] entries --> Pointer to a location in memory that will hold the number of entries + * @param[in] type --> Type of FIFO to poll for entries + * @returns number of entries in the BP to MC FIFO + */ +int hb_bp_get_tx_fifo_entries(int *entries, hb_mc_fifo_tx_t type); + +/* + * Reads the manycore bridge FIFOs in 32-bit chunks to form the 128-bit packet + * @param[in] pkt --> Pointer to the manycore packet + * @param[in] type --> Type of FIFO to read from + * @returns HB_MC_SUCCESS on success, HB_MC_FAIL if FIFO type is unknown + */ +int hb_bp_read_from_mc_bridge(hb_mc_packet_t *pkt, hb_mc_fifo_rx_t type); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libraries/platforms/dromajo-vcs/library.mk b/libraries/platforms/dromajo-vcs/library.mk new file mode 100644 index 000000000..2bd0b1c7f --- /dev/null +++ b/libraries/platforms/dromajo-vcs/library.mk @@ -0,0 +1,202 @@ +# Copyright (c) 2020, University of Washington All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# Redistributions of source code must retain the above copyright notice, this list +# of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above copyright notice, this +# list of conditions and the following disclaimer in the documentation and/or +# other materials provided with the distribution. +# +# Neither the name of the copyright holder nor the names of its contributors may +# be used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# Include the machine configuration to make things clear(er) +include $(BSG_MACHINE_PATH)/Makefile.machine.include + +# RISC-V tools +RV_CC = $(BLACKPARROT_SDK_DIR)/install/bin/riscv64-unknown-elf-dramfs-gcc +RV_CXX = $(BLACKPARROT_SDK_DIR)/install/bin/riscv64-unknown-elf-dramfs-g++ +RV_AR = $(BLACKPARROT_SDK_DIR)/install/bin/riscv64-unknown-elf-dramfs-ar +RV_OBJDUMP = $(BLACKPARROT_SDK_DIR)/install/bin/riscv64-unknown-elf-dramfs-objdump + +DROMAJO_DIR = $(BLACKPARROT_SDK_DIR)/dromajo + +LIB_CXXSOURCES += $(LIBRARIES_PATH)/features/tracer/noimpl/bsg_manycore_tracer.cpp +LIB_CXXSOURCES += $(LIBRARIES_PATH)/features/profiler/noimpl/bsg_manycore_profiler.cpp +LIB_CXXSOURCES += $(BSG_PLATFORM_PATH)/bsg_manycore_platform.cpp + +LIB_CSOURCES += $(BSG_PLATFORM_PATH)/software/src/flockfile.c +LIB_CSOURCES += $(BSG_PLATFORM_PATH)/software/src/funlockfile.c +LIB_CSOURCES += $(BSG_PLATFORM_PATH)/software/src/argp/argp-ba.c +LIB_CSOURCES += $(BSG_PLATFORM_PATH)/software/src/argp/argp-eexst.c +LIB_CSOURCES += $(BSG_PLATFORM_PATH)/software/src/argp/argp-fmtstream.c +LIB_CSOURCES += $(BSG_PLATFORM_PATH)/software/src/argp/argp-fs-xinl.c +LIB_CSOURCES += $(BSG_PLATFORM_PATH)/software/src/argp/argp-help.c +LIB_CSOURCES += $(BSG_PLATFORM_PATH)/software/src/argp/argp-parse.c +LIB_CSOURCES += $(BSG_PLATFORM_PATH)/software/src/argp/argp-pv.c +LIB_CSOURCES += $(BSG_PLATFORM_PATH)/software/src/argp/argp-pvh.c +LIB_CSOURCES += $(BSG_PLATFORM_PATH)/software/src/argp/argp-xinl.c +LIB_CSOURCES += $(BSG_PLATFORM_PATH)/software/src/bp_utils.c +LIB_CSOURCES += $(BSG_PLATFORM_PATH)/software/src/args.c +LIB_CSOURCES += $(BLACKPARROT_SDK_DIR)/perch/bsg_newlib_intf.c +LIB_CSOURCES += $(BLACKPARROT_SDK_DIR)/perch/emulation.c + +LIB_SSOURCES += $(BLACKPARROT_SDK_DIR)/perch/atomics.S +LIB_SSOURCES += $(BLACKPARROT_SDK_DIR)/perch/exception.S +LIB_SSOURCES += $(BLACKPARROT_SDK_DIR)/perch/muldiv.S +LIB_SSOURCES += $(BSG_PLATFORM_PATH)/software/src/crt0.S + +# For now, use the noimpl version of the features +include $(LIBRARIES_PATH)/features/dma/noimpl/feature.mk + +$(DMA_FEATURE_OBJECTS): INCLUDES += -I$(BSG_PLATFORM_PATH)/software/include +$(DMA_FEATURE_OBJECTS): CFLAGS += -march=rv64imafd -mcmodel=medany -mabi=lp64 -D_BSD_SOURCE -D_XOPEN_SOURCE=500 +$(DMA_FEATURE_OBJECTS): CXXFLAGS += -march=rv64imafd -mcmodel=medany -mabi=lp64 -D_BSD_SOURCE -D_XOPEN_SOURCE=500 + +# Add the riscv-newlib specific includes for the library +LIB_PLATFORM_INCLUDES = -I$(BSG_PLATFORM_PATH) +LIB_PLATFORM_INCLUDES += -I$(BSG_PLATFORM_PATH)/software/include +LIB_PLATFORM_INCLUDES += -I$(BLACKPARROT_SDK_DIR)/perch + +$(LIB_OBJECTS) $(LIB_OBJECTS_CUDA_POD_REPL) $(LIB_OBJECTS_REGRESSION): CC = $(RV_CC) +$(LIB_OBJECTS) $(LIB_OBJECTS_CUDA_POD_REPL) $(LIB_OBJECTS_REGRESSION): CXX = $(RV_CXX) + +$(BSG_PLATFORM_PATH)/libbsg_manycore_runtime.a: AR = $(RV_AR) +$(BSG_PLATFORM_PATH)/libbsgmc_cuda_legacy_pod_repl.a: AR = $(RV_AR) +$(BSG_PLATFORM_PATH)/libbsg_manycore_regression.a: AR = $(RV_AR) + +# Base addresses for various segments +DROMAJO_START_ADDR := 0x80000000 +# Dromajo requires that the entry point of the program and the Dromajo start address match +# This is hardcoded in Dromajo (src/riscv_machine.cpp) but can be overridden by providing +# a bootrom using the --bootrom option +BP_DRAM_BASE_ADDR := 0x80000000 +TOP_OF_STACK_ADDR := 0x8F000000 + +# Make the litteFS file system +$(BSG_PLATFORM_PATH)/lfs.cpp: MKLFS = $(BLACKPARROT_SDK_DIR)/install/bin/dramfs_mklfs 128 64 +$(BSG_PLATFORM_PATH)/lfs.cpp: + $(MKLFS) > $@ + +$(BSG_PLATFORM_PATH)/lfs.o: $(BSG_PLATFORM_PATH)/lfs.cpp + $(CXX) $^ -c -o $@ $(CXXFLAGS) $(INCLUDES) + +# Include the compiled manycore binary if one exists +# FIXME: Some caveats with this target --> You have to use it with the *.log target +# In the future we either move away from linking the manycore binary at compile time +# or have a better compilation strategy (requires changes in the test infrastructure) +kernel: + $(if $(BSG_MANYCORE_KERNELS),$(MAKE) $(BSG_PLATFORM_PATH)/mcbin.o,echo "BSG_MANYCORE_KERNELS not defined. Skipping...") + +$(BSG_PLATFORM_PATH)/mcbin.o: CFLAGS := -march=rv64imafd -mabi=lp64 -mcmodel=medany +$(BSG_PLATFORM_PATH)/mcbin.o: INCLUDES := -I$(shell pwd) +$(BSG_PLATFORM_PATH)/mcbin.o: CC := $(RV_CC) +$(BSG_PLATFORM_PATH)/mcbin.o: + cp $(BSG_MANYCORE_KERNELS) manycore.riscv + sed "s|BSG_MANYCORE_KERNELS|\"manycore.riscv\"|g" $(BSG_PLATFORM_PATH)/mcbin.S > genmcbin.S + $(CC) -c -o $@ $(CFLAGS) $(DEFINES) $(INCLUDES) genmcbin.S + +include $(LIBRARIES_PATH)/features/dma/simulation/dramsim3.mk +include $(LIBRARIES_PATH)/features/dma/simulation/libdmamem.mk + +$(LIBRARIES_PATH)/features/dma/simulation/libdmamem.so: INCLUDES := +$(LIBRARIES_PATH)/features/dma/simulation/libdramsim3.so: INCLUDES := + +DROMAJO_CXXSOURCES += $(DROMAJO_DIR)/src/cutils.cpp +DROMAJO_CXXSOURCES += $(DROMAJO_DIR)/src/dromajo_cosim.cpp +DROMAJO_CXXSOURCES += $(DROMAJO_DIR)/src/dromajo_main.cpp +DROMAJO_CXXSOURCES += $(DROMAJO_DIR)/src/dromajo_manycore.cpp +DROMAJO_CXXSOURCES += $(DROMAJO_DIR)/src/dw_apb_uart.cpp +DROMAJO_CXXSOURCES += $(DROMAJO_DIR)/src/elf64.cpp +DROMAJO_CXXSOURCES += $(DROMAJO_DIR)/src/fs.cpp +DROMAJO_CXXSOURCES += $(DROMAJO_DIR)/src/fs_disk.cpp +DROMAJO_CXXSOURCES += $(DROMAJO_DIR)/src/iomem.cpp +DROMAJO_CXXSOURCES += $(DROMAJO_DIR)/src/json.cpp +DROMAJO_CXXSOURCES += $(DROMAJO_DIR)/src/machine.cpp +DROMAJO_CXXSOURCES += $(DROMAJO_DIR)/src/pci.cpp +DROMAJO_CXXSOURCES += $(DROMAJO_DIR)/src/riscv_cpu.cpp +DROMAJO_CXXSOURCES += $(DROMAJO_DIR)/src/riscv_machine.cpp +DROMAJO_CXXSOURCES += $(DROMAJO_DIR)/src/softfp.cpp +DROMAJO_CXXSOURCES += $(DROMAJO_DIR)/src/virtio.cpp + +DROMAJO_OBJECTS += $(patsubst %cpp,%o,$(DROMAJO_CXXSOURCES)) + +$(DROMAJO_OBJECTS): $(BSG_MACHINE_PATH)/Makefile.machine.include +$(DROMAJO_OBJECTS): INCLUDES := -I$(DROMAJO_DIR)/include +$(DROMAJO_OBJECTS): INCLUDES += -I$(BSG_PLATFORM_PATH)/software/include/dromajo +$(DROMAJO_OBJECTS): CXXFLAGS := -std=c++11 -fPIC -Wall -Wno-parentheses -MMD -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_GNU_SOURCE -D__STDC_FORMAT_MACROS -DFIFO_MAX_ELEMENTS=$(BSG_MACHINE_IO_EP_CREDITS) -std=gnu++11 +$(DROMAJO_OBJECTS): LDFLAGS := -fPIC +$(DROMAJO_OBJECTS): CXX = g++ + +PLATFORM_CXXSOURCES += $(BSG_PLATFORM_PATH)/bsg_manycore_simulator.cpp + +PLATFORM_DEFINES = -DHOST_X_COORD=$(BSG_MACHINE_HOST_COORD_X) +PLATFORM_DEFINES += -DHOST_Y_COORD=$(BSG_MACHINE_HOST_COORD_Y) +PLATFORM_DEFINES += -DBP_POD_X=0 -DBP_POD_Y=1 +PLATFORM_DEFINES += -DNUM_DROMAJO_INSTR_PER_TICK=1000 +PLATFORM_DEFINES += -DNUM_TX_FIFO_CHK_PER_TICK=100 +PLATFORM_DEFINES += -DFIFO_MAX_ELEMENTS=$(BSG_MACHINE_IO_EP_CREDITS) +PLATFORM_DEFINES += -DDRAM_BASE_ADDR=$(subst 0x,,$(DROMAJO_START_ADDR)) + +PLATFORM_OBJECTS += $(patsubst %cpp,%o,$(PLATFORM_CXXSOURCES)) +PLATFORM_OBJECTS += $(patsubst %c,%o,$(PLATFORM_CSOURCES)) + +$(PLATFORM_OBJECTS): $(BSG_MACHINE_PATH)/Makefile.machine.include +$(PLATFORM_OBJECTS): INCLUDES := -I$(LIBRARIES_PATH) +$(PLATFORM_OBJECTS): INCLUDES += -I$(EXAMPLES_PATH) +$(PLATFORM_OBJECTS): INCLUDES += -I$(BSG_MACHINE_PATH)/notrace/ +$(PLATFORM_OBJECTS): INCLUDES += -I$(BSG_PLATFORM_PATH) +$(PLATFORM_OBJECTS): INCLUDES += -I$(VCS_HOME)/linux64/lib/ +$(PLATFORM_OBJECTS): INCLUDES += -I$(BSG_MANYCORE_DIR)/testbenches/dpi/ +$(PLATFORM_OBJECTS): INCLUDES += -I$(BASEJUMP_STL_DIR)/bsg_test/ +$(PLATFORM_OBJECTS): INCLUDES += -I$(BLACKPARROT_SDK_DIR)/dromajo/include +$(PLATFORM_OBJECTS): CFLAGS := -std=c11 -fPIC -DVCS -D_GNU_SOURCE -DVERILATOR -D_BSD_SOURCE -D_XOPEN_SOURCE=500 $(PLATFORM_DEFINES) +$(PLATFORM_OBJECTS): CXXFLAGS := -std=c++11 -fPIC -DVCS -D_GNU_SOURCE -DVERILATOR -D_BSD_SOURCE -D_XOPEN_SOURCE=500 $(PLATFORM_DEFINES) +$(PLATFORM_OBJECTS): LDFLAGS := -fPIC +$(PLATFORM_OBJECTS): CXX = g++ + +$(BSG_PLATFORM_PATH)/libbsg_manycore_platform.so.1.0: $(DROMAJO_OBJECTS) +$(BSG_PLATFORM_PATH)/libbsg_manycore_platform.so.1.0: $(PLATFORM_OBJECTS) +$(BSG_PLATFORM_PATH)/libbsg_manycore_platform.so.1.0: $(LIBRARIES_PATH)/features/dma/simulation/libdmamem.so +$(BSG_PLATFORM_PATH)/libbsg_manycore_platform.so.1.0: $(LIBRARIES_PATH)/features/dma/simulation/libdramsim3.so +$(BSG_PLATFORM_PATH)/libbsg_manycore_platform.so.1.0: LDFLAGS := -fPIC +$(BSG_PLATFORM_PATH)/libbsg_manycore_platform.so.1.0: LDFLAGS += -L$(LIBRARIES_PATH)/features/dma/simulation -Wl,-rpath=$(LIBRARIES_PATH)/features/dma/simulation -ldmamem +$(BSG_PLATFORM_PATH)/libbsg_manycore_platform.so.1.0: LDFLAGS += -L$(LIBRARIES_PATH)/features/dma/simulation -Wl,-rpath=$(LIBRARIES_PATH)/features/dma/simulation -ldramsim3 +$(BSG_PLATFORM_PATH)/libbsg_manycore_platform.so.1.0: LD = $(CXX) +$(BSG_PLATFORM_PATH)/libbsg_manycore_platform.so.1.0: + $(LD) -shared -Wl,-soname,$(basename $(notdir $@)) -o $@ $(DROMAJO_OBJECTS) $(PLATFORM_OBJECTS) $(LDFLAGS) + +$(BSG_PLATFORM_PATH)/libbsg_manycore_platform.so.1: %: %.0 + ln -sf $@.0 $@ + +$(BSG_PLATFORM_PATH)/libbsg_manycore_platform.so: %: %.1 + ln -sf $@.1 $@ + +platform.clean: + rm -f $(PLATFORM_OBJECTS) + rm -f $(DROMAJO_OBJECTS) + rm -f $(BSG_PLATFORM_PATH)/lfs.cpp + rm -f $(BSG_PLATFORM_PATH)/lfs.o + rm -f $(BSG_PLATFORM_PATH)/libbsg_manycore_runtime.a + rm -f $(BSG_PLATFORM_PATH)/libbsgmc_cuda_legacy_pod_repl.a + rm -f $(BSG_PLATFORM_PATH)/libbsg_manycore_regression.a + rm -f $(BSG_PLATFORM_PATH)/libbsg_manycore_platform.so.1.0 + rm -f $(BSG_PLATFORM_PATH)/libbsg_manycore_platform.so.1 + rm -f $(BSG_PLATFORM_PATH)/libbsg_manycore_platform.so + +libraries.clean: platform.clean diff --git a/libraries/platforms/dromajo-vcs/link.mk b/libraries/platforms/dromajo-vcs/link.mk new file mode 100644 index 000000000..31cc2ec60 --- /dev/null +++ b/libraries/platforms/dromajo-vcs/link.mk @@ -0,0 +1,184 @@ +# Copyright (c) 2020, University of Washington All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# Redistributions of source code must retain the above copyright notice, this list +# of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above copyright notice, this +# list of conditions and the following disclaimer in the documentation and/or +# other materials provided with the distribution. +# +# Neither the name of the copyright holder nor the names of its contributors may +# be used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# This Makefile fragment defines all of the rules for linking +# aws-vcs binaries + +ORANGE=\033[0;33m +RED=\033[0;31m +NC=\033[0m + +# This file REQUIRES several variables to be set. They are typically set by the +# Makefile that includes this fragment... + +# BSG_PLATFORM_PATH: The path to the testbenches folder in BSG F1 +ifndef BSG_PLATFORM_PATH +$(error $(shell echo -e "$(RED)BSG MAKE ERROR: BSG_PLATFORM_PATH is not defined$(NC)")) +endif + +# hardware.mk is the file list for the simulation RTL. It includes the +# platform specific hardware.mk file. +include $(HARDWARE_PATH)/hardware.mk + +# libraries.mk defines how to build libbsg_manycore_runtime.a, which is +# pre-linked against all other simulation binaries. +include $(LIBRARIES_PATH)/libraries.mk + +# VHEADERS must be compiled before VSOURCES. +VDEFINES += BSG_MACHINE_ORIGIN_X_CORD=$(BSG_MACHINE_ORIGIN_COORD_X) +VDEFINES += BSG_MACHINE_ORIGIN_Y_CORD=$(BSG_MACHINE_ORIGIN_COORD_Y) +VDEFINES += HOST_MODULE_PATH=replicant_tb_top +VDEFINES += BSG_MACHINE_DRAMSIM3_PKG=$(BSG_MACHINE_MEM_DRAMSIM3_PKG) +VLOGAN_SOURCES += $(VHEADERS) $(VSOURCES) +VLOGAN_INCLUDES += $(foreach inc,$(VINCLUDES),+incdir+"$(inc)") +VLOGAN_DEFINES += $(foreach def,$(VDEFINES),+define+"$(def)") +VLOGAN_VFLAGS += -timescale=1ps/1ps +VLOGAN_VFLAGS += -sverilog +VLOGAN_VFLAGS += +systemverilogext+.svh +systemverilogext+.sv +VLOGAN_VFLAGS += +libext+.sv +libext+.v +libext+.vh +libext+.svh +VLOGAN_VFLAGS += -full64 -lca -assert svaext +VLOGAN_VFLAGS += -undef_vcs_macro +VLOGAN_FLAGS = $(VLOGAN_VFLAGS) $(VLOGAN_DEFINES) $(VLOGAN_INCLUDES) $(VLOGAN_SOURCES) + +$(BSG_MACHINE_PATH)/$(BSG_PLATFORM): + mkdir -p $@ + +$(BSG_MACHINE_PATH)/$(BSG_PLATFORM)/synopsys_sim.setup: | $(BSG_MACHINE_PATH)/$(BSG_PLATFORM) + echo "replicant_tb_top : $(BSG_MACHINE_PATH)/$(BSG_PLATFORM)/vcs_simlibs/replicant_tb_top/64" >> $@; + +# The SAIF-generation simulation and fast simulation (exec) turns off the profilers using macros +# so it must be compiled into a separate library. +$(BSG_MACHINE_PATH)/$(BSG_PLATFORM)/synopsys_sim.saifgen: | $(BSG_MACHINE_PATH)/$(BSG_PLATFORM) + echo "replicant_tb_top : $(BSG_MACHINE_PATH)/$(BSG_PLATFORM)/vcs_simlibs/replicant_tb_top_saifgen/64" >> $@; + +$(BSG_MACHINE_PATH)/$(BSG_PLATFORM)/synopsys_sim.exec: | $(BSG_MACHINE_PATH)/$(BSG_PLATFORM) + echo "replicant_tb_top : $(BSG_MACHINE_PATH)/$(BSG_PLATFORM)/vcs_simlibs/replicant_tb_top_exec/64" >> $@; + +$(BSG_MACHINE_PATH)/$(BSG_PLATFORM)/vcs_simlibs/replicant_tb_top/AN.DB: $(BSG_MACHINE_PATH)/$(BSG_PLATFORM)/synopsys_sim.setup $(VLOGAN_SOURCES) + SYNOPSYS_SIM_SETUP=$< vlogan -work replicant_tb_top $(VLOGAN_FLAGS) -l $(BSG_MACHINE_PATH)/$(BSG_PLATFORM)/vlogan.log + +$(BSG_MACHINE_PATH)/$(BSG_PLATFORM)/vcs_simlibs/replicant_tb_top_saifgen/AN.DB $(BSG_MACHINE_PATH)/$(BSG_PLATFORM)/vcs_simlibs/replicant_tb_top_exec/AN.DB: VDEFINES += BSG_MACHINE_DISABLE_VCORE_PROFILING +$(BSG_MACHINE_PATH)/$(BSG_PLATFORM)/vcs_simlibs/replicant_tb_top_saifgen/AN.DB $(BSG_MACHINE_PATH)/$(BSG_PLATFORM)/vcs_simlibs/replicant_tb_top_exec/AN.DB: VDEFINES += BSG_MACHINE_DISABLE_CACHE_PROFILING +$(BSG_MACHINE_PATH)/$(BSG_PLATFORM)/vcs_simlibs/replicant_tb_top_saifgen/AN.DB $(BSG_MACHINE_PATH)/$(BSG_PLATFORM)/vcs_simlibs/replicant_tb_top_exec/AN.DB: VDEFINES += BSG_MACHINE_DISABLE_ROUTER_PROFILING + +$(BSG_MACHINE_PATH)/$(BSG_PLATFORM)/vcs_simlibs/replicant_tb_top_saifgen/AN.DB: VDEFINES += BSG_MACHINE_ENABLE_SAIF +$(BSG_MACHINE_PATH)/$(BSG_PLATFORM)/vcs_simlibs/replicant_tb_top_saifgen/AN.DB: $(BSG_MACHINE_PATH)/$(BSG_PLATFORM)/synopsys_sim.saifgen $(VLOGAN_SOURCES) + SYNOPSYS_SIM_SETUP=$< vlogan -work replicant_tb_top -debug_access+pp $(VLOGAN_FLAGS) -l $(BSG_MACHINE_PATH)/$(BSG_PLATFORM)/vlogan.saifgen.log + +$(BSG_MACHINE_PATH)/$(BSG_PLATFORM)/vcs_simlibs/replicant_tb_top_exec/AN.DB: $(BSG_MACHINE_PATH)/$(BSG_PLATFORM)/synopsys_sim.exec $(VLOGAN_SOURCES) + SYNOPSYS_SIM_SETUP=$< vlogan -work replicant_tb_top $(VLOGAN_FLAGS) -l $(BSG_MACHINE_PATH)/$(BSG_PLATFORM)/vlogan.exec.log + +# libbsg_manycore_platform will be compiled in $(BSG_PLATFORM_PATH) +LDFLAGS += -L$(BSG_PLATFORM_PATH) -Wl,-rpath=$(BSG_PLATFORM_PATH) +LDFLAGS += -lbsg_manycore_platform -lm + +VCS_LDFLAGS += $(foreach def,$(LDFLAGS),-LDFLAGS "$(def)") +VCS_VFLAGS += -M -L -ntb_opts tb_timescale=1ps/1ps -lca +VCS_VFLAGS += -timescale=1ps/1ps -sverilog -full64 -licqueue -q +VCS_VFLAGS += +warn=noLCA_FEATURES_ENABLED +VCS_VFLAGS += +warn=noMC-FCNAFTMI +VCS_VFLAGS += +lint=all,TFIPC-L,noSVA-UA,noSVA-NSVU,noVCDE,noSVA-AECASR +VCS_FLAGS = $(VCS_LDFLAGS) $(VCS_VFLAGS) + +TEST_CSOURCES += $(filter %.c,$(TEST_SOURCES)) +TEST_CXXSOURCES += $(filter %.cpp,$(TEST_SOURCES)) +TEST_OBJECTS += $(TEST_CXXSOURCES:.cpp=.o) +TEST_OBJECTS += $(TEST_CSOURCES:.c=.o) +TEST_OBJECTS += $(BSG_PLATFORM_PATH)/lfs.o +TEST_OBJECTS += $(if $(BSG_MANYCORE_KERNELS),$(BSG_PLATFORM_PATH)/mcbin.o) + +# Generate the linker +linker_gen: PYTHON := python3 +linker_gen: + $(PYTHON) $(BSG_PLATFORM_PATH)/software/linker/linker_gen.py $(BP_DRAM_BASE_ADDR) $(TOP_OF_STACK_ADDR) > $(BSG_PLATFORM_PATH)/software/linker/riscv.ld + +$(BSG_PLATFORM_PATH)/test.riscv: CC = $(RV_CC) +$(BSG_PLATFORM_PATH)/test.riscv: CXX = $(RV_CXX) +$(BSG_PLATFORM_PATH)/test.riscv: LD = $(CXX) +$(BSG_PLATFORM_PATH)/test.riscv: linker_gen +$(BSG_PLATFORM_PATH)/test.riscv: kernel +$(BSG_PLATFORM_PATH)/test.riscv: $(BSG_PLATFORM_PATH)/libbsg_manycore_runtime.a +$(BSG_PLATFORM_PATH)/test.riscv: $(BSG_PLATFORM_PATH)/libbsgmc_cuda_legacy_pod_repl.a +$(BSG_PLATFORM_PATH)/test.riscv: $(BSG_PLATFORM_PATH)/libbsg_manycore_regression.a +$(BSG_PLATFORM_PATH)/test.riscv: $(TEST_OBJECTS) +$(BSG_PLATFORM_PATH)/test.riscv: LDFLAGS := -T$(BSG_PLATFORM_PATH)/software/linker/riscv.ld -L$(BSG_PLATFORM_PATH) -static -nostartfiles -lstdc++ -lbsg_manycore_regression -lbsg_manycore_runtime +$(BSG_PLATFORM_PATH)/test.riscv: + $(LD) -o $@ $(TEST_OBJECTS) $(LDFLAGS) + cp $@ main.elf + +# VCS Generates an executable file by linking the TEST_OBJECTS with +# the the VCS work libraries for the design, and the runtime shared +# libraries + +%.saifgen %.debug: VCS_VFLAGS += -debug_pp +%.debug: VCS_VFLAGS += +plusarg_save +vcs+vcdpluson +vcs+vcdplusmemon +memcbk + +%.debug %.profile: $(BSG_PLATFORM_PATH)/libbsg_manycore_platform.so $(BSG_PLATFORM_PATH)/test.riscv $(BSG_MACHINE_PATH)/$(BSG_PLATFORM)/vcs_simlibs/replicant_tb_top/AN.DB + SYNOPSYS_SIM_SETUP=$(BSG_MACHINE_PATH)/$(BSG_PLATFORM)/synopsys_sim.setup \ + vcs -top replicant_tb_top $(VCS_FLAGS) -Mdirectory=$@.tmp -l $@.vcs.log -o $@ + +%.saifgen: $(BSG_PLATFORM_PATH)/libbsg_manycore_platform.so $(BSG_PLATFORM_PATH)/test.riscv $(BSG_MACHINE_PATH)/$(BSG_PLATFORM)/vcs_simlibs/replicant_tb_top_saifgen/AN.DB + SYNOPSYS_SIM_SETUP=$(BSG_MACHINE_PATH)/$(BSG_PLATFORM)/synopsys_sim.saifgen \ + vcs -top replicant_tb_top $(VCS_FLAGS) -Mdirectory=$@.tmp -l $@.vcs.log -o $@ + +%.exec: $(BSG_PLATFORM_PATH)/libbsg_manycore_platform.so $(BSG_PLATFORM_PATH)/test.riscv $(BSG_MACHINE_PATH)/$(BSG_PLATFORM)/vcs_simlibs/replicant_tb_top_exec/AN.DB + SYNOPSYS_SIM_SETUP=$(BSG_MACHINE_PATH)/$(BSG_PLATFORM)/synopsys_sim.exec \ + vcs -top replicant_tb_top $(VCS_FLAGS) -Mdirectory=$@.tmp -l $@.vcs.log -o $@ + +.PRECIOUS:%.debug %.profile %.saifgen %.exec + +# When running recursive regression, make is launched in independent, +# non-communicating parallel processes that try to build these objects +# in parallel. That is no-bueno. We define REGRESSION_PREBUILD so that +# regression tests can build them before launching parallel +# compilation and execution +REGRESSION_PREBUILD += $(BSG_MACHINE_PATH)/$(BSG_PLATFORM)/vcs_simlibs/replicant_tb_top/AN.DB +REGRESSION_PREBUILD += $(BSG_MACHINE_PATH)/$(BSG_PLATFORM)/vcs_simlibs/replicant_tb_top_exec/AN.DB +REGRESSION_PREBUILD += $(BSG_MACHINE_PATH)/$(BSG_PLATFORM)/vcs_simlibs/replicant_tb_top_saifgen/AN.DB +REGRESSION_PREBUILD += $(BSG_PLATFORM_PATH)/libbsgmc_cuda_legacy_pod_repl.a +REGRESSION_PREBUILD += $(BSG_PLATFORM_PATH)/libbsg_manycore_runtime.a +REGRESSION_PREBUILD += $(BSG_PLATFORM_PATH)/libbsg_manycore_regression.a +REGRESSION_PREBUILD += $(BSG_PLATFORM_PATH)/libbsg_manycore_platform.so + +.PHONY: platform.link.clean +platform.link.clean: + rm -rf $(BSG_MACHINE_PATH)/$(BSG_PLATFORM)/ + rm -rf $(BSG_PLATFORM_PATH)/msg_config + rm -rf ucli.key + rm -rf .cxl* *.jou + rm -rf *.daidir *.tmp + rm -rf *.jou + rm -rf *.vcs.log + rm -rf vc_hdrs.h + rm -rf *.debug *.profile *.saifgen *.exec + rm -rf *.elf + rm -rf $(BSG_PLATFORM_PATH)/*.riscv + rm -rf genmcbin.S manycore.riscv + rm -rf $(BSG_PLATFORM_PATH)/mcbin.o + rm -rf $(BSG_PLATFORM_PATH)/software/linker/riscv.ld + +link.clean: platform.link.clean ; diff --git a/libraries/platforms/dromajo-vcs/mcbin.S b/libraries/platforms/dromajo-vcs/mcbin.S new file mode 100644 index 000000000..e447cb0d4 --- /dev/null +++ b/libraries/platforms/dromajo-vcs/mcbin.S @@ -0,0 +1,42 @@ +# Copyright (c) 2020, University of Washington All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# Redistributions of source code must retain the above copyright notice, this list +# of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above copyright notice, this +# list of conditions and the following disclaimer in the documentation and/or +# other materials provided with the distribution. +# +# Neither the name of the copyright holder nor the names of its contributors may +# be used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# This assembly file includes the manycore binary if there is one + +.section .rodata.manycore + +.globl manycore +.align 4 +.type manycore, @object +manycore: +.incbin BSG_MANYCORE_KERNELS + +.globl manycore_size +.type manycore_size, @object +.align 4 +manycore_size: +.int manycore_size - manycore \ No newline at end of file diff --git a/libraries/platforms/dromajo-vcs/software/include/argp-fmtstream.h b/libraries/platforms/dromajo-vcs/software/include/argp-fmtstream.h new file mode 100644 index 000000000..128558021 --- /dev/null +++ b/libraries/platforms/dromajo-vcs/software/include/argp-fmtstream.h @@ -0,0 +1,278 @@ +// Copyright (c) 2020, University of Washington All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// Redistributions of source code must retain the above copyright notice, this list +// of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above copyright notice, this +// list of conditions and the following disclaimer in the documentation and/or +// other materials provided with the distribution. +// +// Neither the name of the copyright holder nor the names of its contributors may +// be used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// This is a slightly modified copy of the argp library from Newlib + +/* Word-wrapping and line-truncating streams. + Copyright (C) 1997 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Written by Miles Bader . + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +/* This package emulates glibc `line_wrap_stream' semantics for systems that + don't have that. If the system does have it, it is just a wrapper for + that. This header file is only used internally while compiling argp, and + shouldn't be installed. */ + +#ifndef _ARGP_FMTSTREAM_H +#define _ARGP_FMTSTREAM_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#if (_LIBC - 0 && !defined (USE_IN_LIBIO)) \ + || (defined (__GNU_LIBRARY__) && defined (HAVE_LINEWRAP_H)) +/* line_wrap_stream is available, so use that. */ +#define ARGP_FMTSTREAM_USE_LINEWRAP +#endif + +#ifdef ARGP_FMTSTREAM_USE_LINEWRAP +/* Just be a simple wrapper for line_wrap_stream; the semantics are + *slightly* different, as line_wrap_stream doesn't actually make a new + object, it just modifies the given stream (reversibly) to do + line-wrapping. Since we control who uses this code, it doesn't matter. */ + +#include + +typedef FILE *argp_fmtstream_t; + +#define argp_make_fmtstream line_wrap_stream +#define __argp_make_fmtstream line_wrap_stream +#define argp_fmtstream_free line_unwrap_stream +#define __argp_fmtstream_free line_unwrap_stream + +#define __argp_fmtstream_putc(fs,ch) putc(ch,fs) +#define argp_fmtstream_putc(fs,ch) putc(ch,fs) +#define __argp_fmtstream_puts(fs,str) fputs(str,fs) +#define argp_fmtstream_puts(fs,str) fputs(str,fs) +#define __argp_fmtstream_write(fs,str,len) fwrite(str,1,len,fs) +#define argp_fmtstream_write(fs,str,len) fwrite(str,1,len,fs) +#define __argp_fmtstream_printf fprintf +#define argp_fmtstream_printf fprintf + +#define __argp_fmtstream_lmargin line_wrap_lmargin +#define argp_fmtstream_lmargin line_wrap_lmargin +#define __argp_fmtstream_set_lmargin line_wrap_set_lmargin +#define argp_fmtstream_set_lmargin line_wrap_set_lmargin +#define __argp_fmtstream_rmargin line_wrap_rmargin +#define argp_fmtstream_rmargin line_wrap_rmargin +#define __argp_fmtstream_set_rmargin line_wrap_set_rmargin +#define argp_fmtstream_set_rmargin line_wrap_set_rmargin +#define __argp_fmtstream_wmargin line_wrap_wmargin +#define argp_fmtstream_wmargin line_wrap_wmargin +#define __argp_fmtstream_set_wmargin line_wrap_set_wmargin +#define argp_fmtstream_set_wmargin line_wrap_set_wmargin +#define __argp_fmtstream_point line_wrap_point +#define argp_fmtstream_point line_wrap_point + +#else /* !ARGP_FMTSTREAM_USE_LINEWRAP */ +/* Guess we have to define our own version. */ + +#ifndef __const +#define __const const +#endif + +struct argp_fmtstream +{ + FILE *stream; /* The stream we're outputting to. */ + + size_t lmargin, rmargin; /* Left and right margins. */ + ssize_t wmargin; /* Margin to wrap to, or -1 to truncate. */ + + /* Point in buffer to which we've processed for wrapping, but not output. */ + size_t point_offs; + /* Output column at POINT_OFFS, or -1 meaning 0 but don't add lmargin. */ + ssize_t point_col; + + char *buf; /* Output buffer. */ + char *p; /* Current end of text in BUF. */ + char *end; /* Absolute end of BUF. */ +}; + +typedef struct argp_fmtstream *argp_fmtstream_t; + +/* Return an argp_fmtstream that outputs to STREAM, and which prefixes lines + written on it with LMARGIN spaces and limits them to RMARGIN columns + total. If WMARGIN >= 0, words that extend past RMARGIN are wrapped by + replacing the whitespace before them with a newline and WMARGIN spaces. + Otherwise, chars beyond RMARGIN are simply dropped until a newline. + Returns NULL if there was an error. */ +extern argp_fmtstream_t __argp_make_fmtstream (FILE *__stream, + size_t __lmargin, + size_t __rmargin, + ssize_t __wmargin); +extern argp_fmtstream_t argp_make_fmtstream (FILE *__stream, + size_t __lmargin, + size_t __rmargin, + ssize_t __wmargin); + +/* Flush __FS to its stream, and free it (but don't close the stream). */ +extern void __argp_fmtstream_free (argp_fmtstream_t __fs); +extern void argp_fmtstream_free (argp_fmtstream_t __fs); + +extern ssize_t __argp_fmtstream_printf (argp_fmtstream_t __fs, + __const char *__fmt, ...) + __attribute__ ((__format__ (printf, 2, 3))); +extern ssize_t argp_fmtstream_printf (argp_fmtstream_t __fs, + __const char *__fmt, ...) + __attribute__ ((__format__ (printf, 2, 3))); + +/* Access macros for various bits of state. */ +#define argp_fmtstream_lmargin(__fs) ((__fs)->lmargin) +#define argp_fmtstream_rmargin(__fs) ((__fs)->rmargin) +#define argp_fmtstream_wmargin(__fs) ((__fs)->wmargin) +#define __argp_fmtstream_lmargin argp_fmtstream_lmargin +#define __argp_fmtstream_rmargin argp_fmtstream_rmargin +#define __argp_fmtstream_wmargin argp_fmtstream_wmargin + +/* Internal routines. */ +extern void _argp_fmtstream_update (argp_fmtstream_t __fs); +extern void __argp_fmtstream_update (argp_fmtstream_t __fs); +extern int _argp_fmtstream_ensure (argp_fmtstream_t __fs, size_t __amount); +extern int __argp_fmtstream_ensure (argp_fmtstream_t __fs, size_t __amount); + +/* Inline versions of above routines. */ + +#if !_LIBC +#define __argp_fmtstream_putc argp_fmtstream_putc +#define __argp_fmtstream_puts argp_fmtstream_puts +#define __argp_fmtstream_write argp_fmtstream_write +#define __argp_fmtstream_set_lmargin argp_fmtstream_set_lmargin +#define __argp_fmtstream_set_rmargin argp_fmtstream_set_rmargin +#define __argp_fmtstream_set_wmargin argp_fmtstream_set_wmargin +#define __argp_fmtstream_point argp_fmtstream_point +#define __argp_fmtstream_update _argp_fmtstream_update +#define __argp_fmtstream_ensure _argp_fmtstream_ensure +#endif + +static inline size_t __argp_fmtstream_write (argp_fmtstream_t __fs, __const char *__str, size_t __len) +{ + if (__fs->p + __len <= __fs->end || __argp_fmtstream_ensure (__fs, __len)) + { + memcpy (__fs->p, __str, __len); + __fs->p += __len; + return __len; + } + else + return 0; +} + +static inline int __argp_fmtstream_puts (argp_fmtstream_t __fs, __const char *__str) +{ + size_t __len = strlen (__str); + if (__len) + { + size_t __wrote = __argp_fmtstream_write (__fs, __str, __len); + return __wrote == __len ? 0 : -1; + } + else + return 0; +} + +static inline int __argp_fmtstream_putc (argp_fmtstream_t __fs, int __ch) +{ + if (__fs->p < __fs->end || __argp_fmtstream_ensure (__fs, 1)) + return *__fs->p++ = __ch; + else + return EOF; +} + +/* Set __FS's left margin to LMARGIN and return the old value. */ +static inline size_t __argp_fmtstream_set_lmargin (argp_fmtstream_t __fs, size_t __lmargin) +{ + size_t __old; + if ((size_t) (__fs->p - __fs->buf) > __fs->point_offs) + __argp_fmtstream_update (__fs); + __old = __fs->lmargin; + __fs->lmargin = __lmargin; + return __old; +} + +/* Set __FS's right margin to __RMARGIN and return the old value. */ +static inline size_t __argp_fmtstream_set_rmargin (argp_fmtstream_t __fs, size_t __rmargin) +{ + size_t __old; + if ((size_t) (__fs->p - __fs->buf) > __fs->point_offs) + __argp_fmtstream_update (__fs); + __old = __fs->rmargin; + __fs->rmargin = __rmargin; + return __old; +} + +/* Set __FS's wrap margin to __WMARGIN and return the old value. */ +static inline size_t __argp_fmtstream_set_wmargin (argp_fmtstream_t __fs, size_t __wmargin) +{ + size_t __old; + if ((size_t) (__fs->p - __fs->buf) > __fs->point_offs) + __argp_fmtstream_update (__fs); + __old = __fs->wmargin; + __fs->wmargin = __wmargin; + return __old; +} + +/* Return the column number of the current output point in __FS. */ +static inline size_t __argp_fmtstream_point (argp_fmtstream_t __fs) +{ + if ((size_t) (__fs->p - __fs->buf) > __fs->point_offs) + __argp_fmtstream_update (__fs); + return __fs->point_col >= 0 ? __fs->point_col : 0; +} + +#if !_LIBC +#undef __argp_fmtstream_putc +#undef __argp_fmtstream_puts +#undef __argp_fmtstream_write +#undef __argp_fmtstream_set_lmargin +#undef __argp_fmtstream_set_rmargin +#undef __argp_fmtstream_set_wmargin +#undef __argp_fmtstream_point +#undef __argp_fmtstream_update +#undef __argp_fmtstream_ensure +#endif + +#endif /* ARGP_FMTSTREAM_USE_LINEWRAP */ + +#endif /* argp-fmtstream.h */ diff --git a/libraries/platforms/dromajo-vcs/software/include/argp-namefrob.h b/libraries/platforms/dromajo-vcs/software/include/argp-namefrob.h new file mode 100644 index 000000000..cf034e6e8 --- /dev/null +++ b/libraries/platforms/dromajo-vcs/software/include/argp-namefrob.h @@ -0,0 +1,121 @@ +// Copyright (c) 2020, University of Washington All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// Redistributions of source code must retain the above copyright notice, this list +// of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above copyright notice, this +// list of conditions and the following disclaimer in the documentation and/or +// other materials provided with the distribution. +// +// Neither the name of the copyright holder nor the names of its contributors may +// be used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// This is a slightly modified copy of the argp library from Newlib + +/* Name frobnication for compiling argp outside of glibc + Copyright (C) 1997 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Written by Miles Bader . + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#if !_LIBC +/* This code is written for inclusion in gnu-libc, and uses names in the + namespace reserved for libc. If we're not compiling in libc, define those + names to be the normal ones instead. */ + +/* argp-parse functions */ +#undef __argp_parse +#define __argp_parse argp_parse +#undef __option_is_end +#define __option_is_end _option_is_end +#undef __option_is_short +#define __option_is_short _option_is_short +#undef __argp_input +#define __argp_input _argp_input + +/* argp-help functions */ +#undef __argp_help +#define __argp_help argp_help +#undef __argp_error +#define __argp_error argp_error +#undef __argp_failure +#define __argp_failure argp_failure +#undef __argp_state_help +#define __argp_state_help argp_state_help +#undef __argp_usage +#define __argp_usage argp_usage + +/* argp-fmtstream functions */ +#undef __argp_make_fmtstream +#define __argp_make_fmtstream argp_make_fmtstream +#undef __argp_fmtstream_free +#define __argp_fmtstream_free argp_fmtstream_free +#undef __argp_fmtstream_putc +#define __argp_fmtstream_putc argp_fmtstream_putc +#undef __argp_fmtstream_puts +#define __argp_fmtstream_puts argp_fmtstream_puts +#undef __argp_fmtstream_write +#define __argp_fmtstream_write argp_fmtstream_write +#undef __argp_fmtstream_printf +#define __argp_fmtstream_printf argp_fmtstream_printf +#undef __argp_fmtstream_set_lmargin +#define __argp_fmtstream_set_lmargin argp_fmtstream_set_lmargin +#undef __argp_fmtstream_set_rmargin +#define __argp_fmtstream_set_rmargin argp_fmtstream_set_rmargin +#undef __argp_fmtstream_set_wmargin +#define __argp_fmtstream_set_wmargin argp_fmtstream_set_wmargin +#undef __argp_fmtstream_point +#define __argp_fmtstream_point argp_fmtstream_point +#undef __argp_fmtstream_update +#define __argp_fmtstream_update _argp_fmtstream_update +#undef __argp_fmtstream_ensure +#define __argp_fmtstream_ensure _argp_fmtstream_ensure +#undef __argp_fmtstream_lmargin +#define __argp_fmtstream_lmargin argp_fmtstream_lmargin +#undef __argp_fmtstream_rmargin +#define __argp_fmtstream_rmargin argp_fmtstream_rmargin +#undef __argp_fmtstream_wmargin +#define __argp_fmtstream_wmargin argp_fmtstream_wmargin + +/* normal libc functions we call */ +#undef __sleep +#define __sleep sleep +#undef __strcasecmp +#define __strcasecmp strcasecmp +#undef __vsnprintf +#define __vsnprintf vsnprintf + +#endif /* !_LIBC */ + +#ifndef __set_errno +#define __set_errno(e) (errno = (e)) +#endif diff --git a/libraries/platforms/dromajo-vcs/software/include/argp.h b/libraries/platforms/dromajo-vcs/software/include/argp.h new file mode 100644 index 000000000..43dde05fa --- /dev/null +++ b/libraries/platforms/dromajo-vcs/software/include/argp.h @@ -0,0 +1,597 @@ +// Copyright (c) 2020, University of Washington All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// Redistributions of source code must retain the above copyright notice, this list +// of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above copyright notice, this +// list of conditions and the following disclaimer in the documentation and/or +// other materials provided with the distribution. +// +// Neither the name of the copyright holder nor the names of its contributors may +// be used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// This is a slightly modified copy of the argp library from Newlib + +/* Hierarchial argument parsing, layered over getopt. + Copyright (C) 1995, 1996, 1997, 1998, 1999 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Written by Miles Bader . + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#ifndef _ARGP_H +#define _ARGP_H + +#include +#include +#include + +#define __need_error_t +#include + +char *program_invocation_name; +char *program_invocation_short_name; + +#ifndef __const +# define __const const +#endif + +#ifndef __error_t_defined +typedef int error_t; +# define __error_t_defined +#endif + +#undef __THROW +#define __THROW + +#ifndef __NTH +# define __NTH(fct) fct __THROW +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + +/* A description of a particular option. A pointer to an array of + these is passed in the OPTIONS field of an argp structure. Each option + entry can correspond to one long option and/or one short option; more + names for the same option can be added by following an entry in an option + array with options having the OPTION_ALIAS flag set. */ +struct argp_option +{ + /* The long option name. For more than one name for the same option, you + can use following options with the OPTION_ALIAS flag set. */ + __const char *name; + + /* What key is returned for this option. If > 0 and printable, then it's + also accepted as a short option. */ + int key; + + /* If non-NULL, this is the name of the argument associated with this + option, which is required unless the OPTION_ARG_OPTIONAL flag is set. */ + __const char *arg; + + /* OPTION_ flags. */ + int flags; + + /* The doc string for this option. If both NAME and KEY are 0, This string + will be printed outdented from the normal option column, making it + useful as a group header (it will be the first thing printed in its + group); in this usage, it's conventional to end the string with a `:'. */ + __const char *doc; + + /* The group this option is in. In a long help message, options are sorted + alphabetically within each group, and the groups presented in the order + 0, 1, 2, ..., n, -m, ..., -2, -1. Every entry in an options array with + if this field 0 will inherit the group number of the previous entry, or + zero if it's the first one, unless its a group header (NAME and KEY both + 0), in which case, the previous entry + 1 is the default. Automagic + options such as --help are put into group -1. */ + int group; +}; + +/* The argument associated with this option is optional. */ +#define OPTION_ARG_OPTIONAL 0x1 + +/* This option isn't displayed in any help messages. */ +#define OPTION_HIDDEN 0x2 + +/* This option is an alias for the closest previous non-alias option. This + means that it will be displayed in the same help entry, and will inherit + fields other than NAME and KEY from the aliased option. */ +#define OPTION_ALIAS 0x4 + +/* This option isn't actually an option (and so should be ignored by the + actual option parser), but rather an arbitrary piece of documentation that + should be displayed in much the same manner as the options. If this flag + is set, then the option NAME field is displayed unmodified (e.g., no `--' + prefix is added) at the left-margin (where a *short* option would normally + be displayed), and the documentation string in the normal place. For + purposes of sorting, any leading whitespace and puncuation is ignored, + except that if the first non-whitespace character is not `-', this entry + is displayed after all options (and OPTION_DOC entries with a leading `-') + in the same group. */ +#define OPTION_DOC 0x8 + +/* This option shouldn't be included in `long' usage messages (but is still + included in help messages). This is mainly intended for options that are + completely documented in an argp's ARGS_DOC field, in which case including + the option in the generic usage list would be redundant. For instance, + if ARGS_DOC is "FOO BAR\n-x BLAH", and the `-x' option's purpose is to + distinguish these two cases, -x should probably be marked + OPTION_NO_USAGE. */ +#define OPTION_NO_USAGE 0x10 + +struct argp; /* fwd declare this type */ +struct argp_state; /* " */ +struct argp_child; /* " */ + +/* The type of a pointer to an argp parsing function. */ +typedef error_t (*argp_parser_t) (int key, char *arg, + struct argp_state *state); + +/* What to return for unrecognized keys. For special ARGP_KEY_ keys, such + returns will simply be ignored. For user keys, this error will be turned + into EINVAL (if the call to argp_parse is such that errors are propagated + back to the user instead of exiting); returning EINVAL itself would result + in an immediate stop to parsing in *all* cases. */ +#define ARGP_ERR_UNKNOWN E2BIG /* Hurd should never need E2BIG. XXX */ + +/* Special values for the KEY argument to an argument parsing function. + ARGP_ERR_UNKNOWN should be returned if they aren't understood. + + The sequence of keys to a parsing function is either (where each + uppercased word should be prefixed by `ARGP_KEY_' and opt is a user key): + + INIT opt... NO_ARGS END SUCCESS -- No non-option arguments at all + or INIT (opt | ARG)... END SUCCESS -- All non-option args parsed + or INIT (opt | ARG)... SUCCESS -- Some non-option arg unrecognized + + The third case is where every parser returned ARGP_KEY_UNKNOWN for an + argument, in which case parsing stops at that argument (returning the + unparsed arguments to the caller of argp_parse if requested, or stopping + with an error message if not). + + If an error occurs (either detected by argp, or because the parsing + function returned an error value), then the parser is called with + ARGP_KEY_ERROR, and no further calls are made. */ + +/* This is not an option at all, but rather a command line argument. If a + parser receiving this key returns success, the fact is recorded, and the + ARGP_KEY_NO_ARGS case won't be used. HOWEVER, if while processing the + argument, a parser function decrements the NEXT field of the state it's + passed, the option won't be considered processed; this is to allow you to + actually modify the argument (perhaps into an option), and have it + processed again. */ +#define ARGP_KEY_ARG 0 +/* There are remaining arguments not parsed by any parser, which may be found + starting at (STATE->argv + STATE->next). If success is returned, but + STATE->next left untouched, it's assumed that all arguments were consume, + otherwise, the parser should adjust STATE->next to reflect any arguments + consumed. */ +#define ARGP_KEY_ARGS 0x1000006 +/* There are no more command line arguments at all. */ +#define ARGP_KEY_END 0x1000001 +/* Because it's common to want to do some special processing if there aren't + any non-option args, user parsers are called with this key if they didn't + successfully process any non-option arguments. Called just before + ARGP_KEY_END (where more general validity checks on previously parsed + arguments can take place). */ +#define ARGP_KEY_NO_ARGS 0x1000002 +/* Passed in before any parsing is done. Afterwards, the values of each + element of the CHILD_INPUT field, if any, in the state structure is + copied to each child's state to be the initial value of the INPUT field. */ +#define ARGP_KEY_INIT 0x1000003 +/* Use after all other keys, including SUCCESS & END. */ +#define ARGP_KEY_FINI 0x1000007 +/* Passed in when parsing has successfully been completed (even if there are + still arguments remaining). */ +#define ARGP_KEY_SUCCESS 0x1000004 +/* Passed in if an error occurs. */ +#define ARGP_KEY_ERROR 0x1000005 + +/* An argp structure contains a set of options declarations, a function to + deal with parsing one, documentation string, a possible vector of child + argp's, and perhaps a function to filter help output. When actually + parsing options, getopt is called with the union of all the argp + structures chained together through their CHILD pointers, with conflicts + being resolved in favor of the first occurrence in the chain. */ +struct argp +{ + /* An array of argp_option structures, terminated by an entry with both + NAME and KEY having a value of 0. */ + __const struct argp_option *options; + + /* What to do with an option from this structure. KEY is the key + associated with the option, and ARG is any associated argument (NULL if + none was supplied). If KEY isn't understood, ARGP_ERR_UNKNOWN should be + returned. If a non-zero, non-ARGP_ERR_UNKNOWN value is returned, then + parsing is stopped immediately, and that value is returned from + argp_parse(). For special (non-user-supplied) values of KEY, see the + ARGP_KEY_ definitions below. */ + argp_parser_t parser; + + /* A string describing what other arguments are wanted by this program. It + is only used by argp_usage to print the `Usage:' message. If it + contains newlines, the strings separated by them are considered + alternative usage patterns, and printed on separate lines (lines after + the first are prefix by ` or: ' instead of `Usage:'). */ + __const char *args_doc; + + /* If non-NULL, a string containing extra text to be printed before and + after the options in a long help message (separated by a vertical tab + `\v' character). */ + __const char *doc; + + /* A vector of argp_children structures, terminated by a member with a 0 + argp field, pointing to child argps should be parsed with this one. Any + conflicts are resolved in favor of this argp, or early argps in the + CHILDREN list. This field is useful if you use libraries that supply + their own argp structure, which you want to use in conjunction with your + own. */ + __const struct argp_child *children; + + /* If non-zero, this should be a function to filter the output of help + messages. KEY is either a key from an option, in which case TEXT is + that option's help text, or a special key from the ARGP_KEY_HELP_ + defines, below, describing which other help text TEXT is. The function + should return either TEXT, if it should be used as-is, a replacement + string, which should be malloced, and will be freed by argp, or NULL, + meaning `print nothing'. The value for TEXT is *after* any translation + has been done, so if any of the replacement text also needs translation, + that should be done by the filter function. INPUT is either the input + supplied to argp_parse, or NULL, if argp_help was called directly. */ + char *(*help_filter) (int __key, __const char *__text, void *__input); + + /* If non-zero the strings used in the argp library are translated using + the domain described by this string. Otherwise the currently installed + default domain is used. */ + const char *argp_domain; +}; + +/* Possible KEY arguments to a help filter function. */ +#define ARGP_KEY_HELP_PRE_DOC 0x2000001 /* Help text preceeding options. */ +#define ARGP_KEY_HELP_POST_DOC 0x2000002 /* Help text following options. */ +#define ARGP_KEY_HELP_HEADER 0x2000003 /* Option header string. */ +#define ARGP_KEY_HELP_EXTRA 0x2000004 /* After all other documentation; + TEXT is NULL for this key. */ +/* Explanatory note emitted when duplicate option arguments have been + suppressed. */ +#define ARGP_KEY_HELP_DUP_ARGS_NOTE 0x2000005 +#define ARGP_KEY_HELP_ARGS_DOC 0x2000006 /* Argument doc string. */ + +/* When an argp has a non-zero CHILDREN field, it should point to a vector of + argp_child structures, each of which describes a subsidiary argp. */ +struct argp_child +{ + /* The child parser. */ + __const struct argp *argp; + + /* Flags for this child. */ + int flags; + + /* If non-zero, an optional header to be printed in help output before the + child options. As a side-effect, a non-zero value forces the child + options to be grouped together; to achieve this effect without actually + printing a header string, use a value of "". */ + __const char *header; + + /* Where to group the child options relative to the other (`consolidated') + options in the parent argp; the values are the same as the GROUP field + in argp_option structs, but all child-groupings follow parent options at + a particular group level. If both this field and HEADER are zero, then + they aren't grouped at all, but rather merged with the parent options + (merging the child's grouping levels with the parents). */ + int group; +}; + +/* Parsing state. This is provided to parsing functions called by argp, + which may examine and, as noted, modify fields. */ +struct argp_state +{ + /* The top level ARGP being parsed. */ + __const struct argp *root_argp; + + /* The argument vector being parsed. May be modified. */ + int argc; + char **argv; + + /* The index in ARGV of the next arg that to be parsed. May be modified. */ + int next; + + /* The flags supplied to argp_parse. May be modified. */ + unsigned flags; + + /* While calling a parsing function with a key of ARGP_KEY_ARG, this is the + number of the current arg, starting at zero, and incremented after each + such call returns. At all other times, this is the number of such + arguments that have been processed. */ + unsigned arg_num; + + /* If non-zero, the index in ARGV of the first argument following a special + `--' argument (which prevents anything following being interpreted as an + option). Only set once argument parsing has proceeded past this point. */ + int quoted; + + /* An arbitrary pointer passed in from the user. */ + void *input; + /* Values to pass to child parsers. This vector will be the same length as + the number of children for the current parser. */ + void **child_inputs; + + /* For the parser's use. Initialized to 0. */ + void *hook; + + /* The name used when printing messages. This is initialized to ARGV[0], + or PROGRAM_INVOCATION_NAME if that is unavailable. */ + char *name; + + /* Streams used when argp prints something. */ + FILE *err_stream; /* For errors; initialized to stderr. */ + FILE *out_stream; /* For information; initialized to stdout. */ + + void *pstate; /* Private, for use by argp. */ +}; + +/* Flags for argp_parse (note that the defaults are those that are + convenient for program command line parsing): */ + +/* Don't ignore the first element of ARGV. Normally (and always unless + ARGP_NO_ERRS is set) the first element of the argument vector is + skipped for option parsing purposes, as it corresponds to the program name + in a command line. */ +#define ARGP_PARSE_ARGV0 0x01 + +/* Don't print error messages for unknown options to stderr; unless this flag + is set, ARGP_PARSE_ARGV0 is ignored, as ARGV[0] is used as the program + name in the error messages. This flag implies ARGP_NO_EXIT (on the + assumption that silent exiting upon errors is bad behaviour). */ +#define ARGP_NO_ERRS 0x02 + +/* Don't parse any non-option args. Normally non-option args are parsed by + calling the parse functions with a key of ARGP_KEY_ARG, and the actual arg + as the value. Since it's impossible to know which parse function wants to + handle it, each one is called in turn, until one returns 0 or an error + other than ARGP_ERR_UNKNOWN; if an argument is handled by no one, the + argp_parse returns prematurely (but with a return value of 0). If all + args have been parsed without error, all parsing functions are called one + last time with a key of ARGP_KEY_END. This flag needn't normally be set, + as the normal behavior is to stop parsing as soon as some argument can't + be handled. */ +#define ARGP_NO_ARGS 0x04 + +/* Parse options and arguments in the same order they occur on the command + line -- normally they're rearranged so that all options come first. */ +#define ARGP_IN_ORDER 0x08 + +/* Don't provide the standard long option --help, which causes usage and + option help information to be output to stdout, and exit (0) called. */ +#define ARGP_NO_HELP 0x10 + +/* Don't exit on errors (they may still result in error messages). */ +#define ARGP_NO_EXIT 0x20 + +/* Use the gnu getopt `long-only' rules for parsing arguments. */ +#define ARGP_LONG_ONLY 0x40 + +/* Turns off any message-printing/exiting options. */ +#define ARGP_SILENT (ARGP_NO_EXIT | ARGP_NO_ERRS | ARGP_NO_HELP) + +/* Parse the options strings in ARGC & ARGV according to the options in ARGP. + FLAGS is one of the ARGP_ flags above. If ARG_INDEX is non-NULL, the + index in ARGV of the first unparsed option is returned in it. If an + unknown option is present, ARGP_ERR_UNKNOWN is returned; if some parser + routine returned a non-zero value, it is returned; otherwise 0 is + returned. This function may also call exit unless the ARGP_NO_HELP flag + is set. INPUT is a pointer to a value to be passed in to the parser. */ +extern error_t argp_parse (__const struct argp *__restrict __argp, + int __argc, char **__restrict __argv, + unsigned __flags, int *__restrict __arg_index, + void *__restrict __input) __THROW; +extern error_t __argp_parse (__const struct argp *__restrict __argp, + int __argc, char **__restrict __argv, + unsigned __flags, int *__restrict __arg_index, + void *__restrict __input) __THROW; + +/* Global variables. */ + +/* If defined or set by the user program to a non-zero value, then a default + option --version is added (unless the ARGP_NO_HELP flag is used), which + will print this string followed by a newline and exit (unless the + ARGP_NO_EXIT flag is used). Overridden by ARGP_PROGRAM_VERSION_HOOK. */ +extern __const char *argp_program_version; + +/* If defined or set by the user program to a non-zero value, then a default + option --version is added (unless the ARGP_NO_HELP flag is used), which + calls this function with a stream to print the version to and a pointer to + the current parsing state, and then exits (unless the ARGP_NO_EXIT flag is + used). This variable takes precedent over ARGP_PROGRAM_VERSION. */ +extern void (*argp_program_version_hook) (FILE *__restrict __stream, + struct argp_state *__restrict + __state); + +/* If defined or set by the user program, it should point to string that is + the bug-reporting address for the program. It will be printed by + argp_help if the ARGP_HELP_BUG_ADDR flag is set (as it is by various + standard help messages), embedded in a sentence that says something like + `Report bugs to ADDR.'. */ +extern __const char *argp_program_bug_address; + +/* The exit status that argp will use when exiting due to a parsing error. + If not defined or set by the user program, this defaults to EX_USAGE from + . */ +extern error_t argp_err_exit_status; + +/* Flags for argp_help. */ +#define ARGP_HELP_USAGE 0x01 /* a Usage: message. */ +#define ARGP_HELP_SHORT_USAGE 0x02 /* " but don't actually print options. */ +#define ARGP_HELP_SEE 0x04 /* a `Try ... for more help' message. */ +#define ARGP_HELP_LONG 0x08 /* a long help message. */ +#define ARGP_HELP_PRE_DOC 0x10 /* doc string preceding long help. */ +#define ARGP_HELP_POST_DOC 0x20 /* doc string following long help. */ +#define ARGP_HELP_DOC (ARGP_HELP_PRE_DOC | ARGP_HELP_POST_DOC) +#define ARGP_HELP_BUG_ADDR 0x40 /* bug report address */ +#define ARGP_HELP_LONG_ONLY 0x80 /* modify output appropriately to + reflect ARGP_LONG_ONLY mode. */ + +/* These ARGP_HELP flags are only understood by argp_state_help. */ +#define ARGP_HELP_EXIT_ERR 0x100 /* Call exit(1) instead of returning. */ +#define ARGP_HELP_EXIT_OK 0x200 /* Call exit(0) instead of returning. */ + +/* The standard thing to do after a program command line parsing error, if an + error message has already been printed. */ +#define ARGP_HELP_STD_ERR \ + (ARGP_HELP_SEE | ARGP_HELP_EXIT_ERR) +/* The standard thing to do after a program command line parsing error, if no + more specific error message has been printed. */ +#define ARGP_HELP_STD_USAGE \ + (ARGP_HELP_SHORT_USAGE | ARGP_HELP_SEE | ARGP_HELP_EXIT_ERR) +/* The standard thing to do in response to a --help option. */ +#define ARGP_HELP_STD_HELP \ + (ARGP_HELP_SHORT_USAGE | ARGP_HELP_LONG | ARGP_HELP_EXIT_OK \ + | ARGP_HELP_DOC | ARGP_HELP_BUG_ADDR) + +/* Output a usage message for ARGP to STREAM. FLAGS are from the set + ARGP_HELP_*. */ +extern void argp_help (__const struct argp *__restrict __argp, + FILE *__restrict __stream, + unsigned __flags, char *__restrict __name) __THROW; +extern void __argp_help (__const struct argp *__restrict __argp, + FILE *__restrict __stream, unsigned __flags, + char *__name) __THROW; + +/* The following routines are intended to be called from within an argp + parsing routine (thus taking an argp_state structure as the first + argument). They may or may not print an error message and exit, depending + on the flags in STATE -- in any case, the caller should be prepared for + them *not* to exit, and should return an appropiate error after calling + them. [argp_usage & argp_error should probably be called argp_state_..., + but they're used often enough that they should be short] */ + +/* Output, if appropriate, a usage message for STATE to STREAM. FLAGS are + from the set ARGP_HELP_*. */ +extern void argp_state_help (__const struct argp_state *__restrict __state, + FILE *__restrict __stream, + unsigned int __flags) __THROW; +extern void __argp_state_help (__const struct argp_state *__restrict __state, + FILE *__restrict __stream, + unsigned int __flags) __THROW; + +/* If appropriate, print the printf string FMT and following args, preceded + by the program name and `:', to stderr, and followed by a `Try ... --help' + message, then exit (1). */ +extern void argp_error (__const struct argp_state *__restrict __state, + __const char *__restrict __fmt, ...) __THROW + __attribute__ ((__format__ (__printf__, 2, 3))); +extern void __argp_error (__const struct argp_state *__restrict __state, + __const char *__restrict __fmt, ...) __THROW + __attribute__ ((__format__ (__printf__, 2, 3))); + +/* Similar to the standard gnu error-reporting function error(), but will + respect the ARGP_NO_EXIT and ARGP_NO_ERRS flags in STATE, and will print + to STATE->err_stream. This is useful for argument parsing code that is + shared between program startup (when exiting is desired) and runtime + option parsing (when typically an error code is returned instead). The + difference between this function and argp_error is that the latter is for + *parsing errors*, and the former is for other problems that occur during + parsing but don't reflect a (syntactic) problem with the input. */ +extern void argp_failure (__const struct argp_state *__restrict __state, + int __status, int __errnum, + __const char *__restrict __fmt, ...) __THROW + __attribute__ ((__format__ (__printf__, 4, 5))); +extern void __argp_failure (__const struct argp_state *__restrict __state, + int __status, int __errnum, + __const char *__restrict __fmt, ...) __THROW + __attribute__ ((__format__ (__printf__, 4, 5))); + +/* Return the input field for ARGP in the parser corresponding to STATE; used + by the help routines. */ +extern void *_argp_input (__const struct argp *__restrict __argp, + __const struct argp_state *__restrict __state) + __THROW; +extern void *__argp_input (__const struct argp *__restrict __argp, + __const struct argp_state *__restrict __state) + __THROW; + +// Sripathi: +// Always define functions in a header file with the static modifier. +// Defining functions with "extern inline" in a header file is just wrong. +// Switch to using "static inline" to make the function visible wherever +// this header file is included + +# if !_LIBC +# define __argp_usage argp_usage +# define __argp_state_help argp_state_help +# define __option_is_short _option_is_short +# define __option_is_end _option_is_end +# endif + +/* Possibly output the standard usage message for ARGP to stderr and exit. */ +static inline void +__argp_usage (__const struct argp_state *__state) __THROW +{ + __argp_state_help (__state, stderr, ARGP_HELP_STD_USAGE); +} + +/* Returns true if the option OPT is a valid short option. */ +static inline int +__option_is_short (__const struct argp_option *__opt) __THROW +{ + if (__opt->flags & OPTION_DOC) + return 0; + else + { + int __key = __opt->key; + return __key > 0 && isprint (__key); + } +} + +/* Returns true if the option OPT is in fact the last (unused) entry in an + options array. */ +static inline int +__option_is_end (__const struct argp_option *__opt) __THROW +{ + return !__opt->key && !__opt->name && !__opt->doc && !__opt->group; +} + +# if !_LIBC +# undef __argp_usage +# undef __argp_state_help +# undef __option_is_short +# undef __option_is_end +# endif + +#ifdef __cplusplus +} +#endif + +#endif /* argp.h */ diff --git a/libraries/platforms/dromajo-vcs/software/include/bp_utils.h b/libraries/platforms/dromajo-vcs/software/include/bp_utils.h new file mode 100644 index 000000000..2fdc5c495 --- /dev/null +++ b/libraries/platforms/dromajo-vcs/software/include/bp_utils.h @@ -0,0 +1,82 @@ +// Copyright (c) 2020, University of Washington All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// Redistributions of source code must retain the above copyright notice, this list +// of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above copyright notice, this +// list of conditions and the following disclaimer in the documentation and/or +// other materials provided with the distribution. +// +// Neither the name of the copyright holder nor the names of its contributors may +// be used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// BlackParrot utilities header file +// Commonly used BlackParrot functions with modifications for +// the manycore platform + +#ifndef BP_UTILS_H +#define BP_UTILS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#define BP_CFG_BASE_ADDR 0x00200000 + +/**************************** BlackParrot helper functions ****************************/ + +/* + * Get hardware thread ID + * @returns result of reading the mhartid register + */ +uint64_t bp_get_hart(); + +/* + * Checks to see if a barrier is finished + * @param[in] barrier_address --> An address in memory that all cores to write to after hitting the barrier + * @param[in] total_num_cores --> Number of cores + */ +void bp_barrier_end(volatile uint64_t *barrier_address, uint64_t total_num_cores); + +/* + * Sends a hex digit to the host to print + * @param[in] hex --> Hex digit to print + */ +void bp_hprint(uint8_t hex); + +/* + * Sends a character to the host to print + * @param[in] ch --> Character to print + */ +void bp_cprint(uint8_t ch); + +/* + * Sends a finish packet to the host + * @param[in] code --> Finish code + */ +void bp_finish(int16_t code); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libraries/platforms/dromajo-vcs/software/include/dromajo/config.h b/libraries/platforms/dromajo-vcs/software/include/dromajo/config.h new file mode 100644 index 000000000..8ae55c8ff --- /dev/null +++ b/libraries/platforms/dromajo-vcs/software/include/dromajo/config.h @@ -0,0 +1,30 @@ +// Copyright (c) 2020, University of Washington All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// Redistributions of source code must retain the above copyright notice, this list +// of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above copyright notice, this +// list of conditions and the following disclaimer in the documentation and/or +// other materials provided with the distribution. +// +// Neither the name of the copyright holder nor the names of its contributors may +// be used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Config Header for Dromajo + +#define CONFIG_VERSION "Dromajo-0.1" diff --git a/libraries/platforms/dromajo-vcs/software/include/endian.h b/libraries/platforms/dromajo-vcs/software/include/endian.h new file mode 100644 index 000000000..db84ae0a0 --- /dev/null +++ b/libraries/platforms/dromajo-vcs/software/include/endian.h @@ -0,0 +1,51 @@ +// Copyright (c) 2020, University of Washington All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// Redistributions of source code must retain the above copyright notice, this list +// of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above copyright notice, this +// list of conditions and the following disclaimer in the documentation and/or +// other materials provided with the distribution. +// +// Neither the name of the copyright holder nor the names of its contributors may +// be used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Note: Users should only use this file for compiling with NEWLIB + +// In GLIBC, this header file is present in the default include path however +// in NEWLIB, the same header file is present in /machine/ +// therefore, this dummy file is used to actually call the correct header file in +// NEWLIB whilst still maintaining the same interface as GLIBC. This is done to prevent +// changes to the CUDA-lite library code. + +#ifndef _ENDIAN_H +#define _ENDIAN_H + +#include + +#if defined(_DEFAULT_SOURCE) && (!defined(__ASSEMBLER__)) +// BlackParrot is Little Endian +#define le16toh(_x) ((__uint16_t)(_x)) +#define htole16(_x) ((__uint16_t)(_x)) +#define le32toh(_x) ((__uint32_t)(_x)) +#define htole32(_x) ((__uint32_t)(_x)) +#define le64toh(_x) ((__uint64_t)(_x)) +#define htole64(_x) ((__uint64_t)(_x)) +#endif + +#endif/* _ENDIAN_H__ */ diff --git a/libraries/platforms/dromajo-vcs/software/include/features.h b/libraries/platforms/dromajo-vcs/software/include/features.h new file mode 100644 index 000000000..acada50ca --- /dev/null +++ b/libraries/platforms/dromajo-vcs/software/include/features.h @@ -0,0 +1,41 @@ +// Copyright (c) 2020, University of Washington All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// Redistributions of source code must retain the above copyright notice, this list +// of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above copyright notice, this +// list of conditions and the following disclaimer in the documentation and/or +// other materials provided with the distribution. +// +// Neither the name of the copyright holder nor the names of its contributors may +// be used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Note: Users should only use this file for compiling with NEWLIB + +// In GLIBC, this header file is present in the default include path however +// in NEWLIB, the same header file is present in /sys/ +// therefore, this dummy file is used to actually call the correct header file in +// NEWLIB whilst still maintaining the same interface as GLIBC. This is done to prevent +// changes to the CUDA-lite library code. + +#ifndef _FEATURES_H +#define _FEATURES_H + +#include + +#endif /* _FEATURES_H */ diff --git a/libraries/platforms/dromajo-vcs/software/include/sysexits.h b/libraries/platforms/dromajo-vcs/software/include/sysexits.h new file mode 100644 index 000000000..8ac6e6ad9 --- /dev/null +++ b/libraries/platforms/dromajo-vcs/software/include/sysexits.h @@ -0,0 +1,143 @@ +// Copyright (c) 2020, University of Washington All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// Redistributions of source code must retain the above copyright notice, this list +// of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above copyright notice, this +// list of conditions and the following disclaimer in the documentation and/or +// other materials provided with the distribution. +// +// Neither the name of the copyright holder nor the names of its contributors may +// be used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// A copy of the system exit error codes file from GCC + +/* + * Copyright (c) 1987, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)sysexits.h 8.1 (Berkeley) 6/2/93 + */ + +#ifndef _SYSEXITS_H +#define _SYSEXITS_H 1 + +/* + * SYSEXITS.H -- Exit status codes for system programs. + * + * This include file attempts to categorize possible error + * exit statuses for system programs, notably delivermail + * and the Berkeley network. + * + * Error numbers begin at EX__BASE to reduce the possibility of + * clashing with other exit statuses that random programs may + * already return. The meaning of the codes is approximately + * as follows: + * + * EX_USAGE -- The command was used incorrectly, e.g., with + * the wrong number of arguments, a bad flag, a bad + * syntax in a parameter, or whatever. + * EX_DATAERR -- The input data was incorrect in some way. + * This should only be used for user's data & not + * system files. + * EX_NOINPUT -- An input file (not a system file) did not + * exist or was not readable. This could also include + * errors like "No message" to a mailer (if it cared + * to catch it). + * EX_NOUSER -- The user specified did not exist. This might + * be used for mail addresses or remote logins. + * EX_NOHOST -- The host specified did not exist. This is used + * in mail addresses or network requests. + * EX_UNAVAILABLE -- A service is unavailable. This can occur + * if a support program or file does not exist. This + * can also be used as a catchall message when something + * you wanted to do doesn't work, but you don't know + * why. + * EX_SOFTWARE -- An internal software error has been detected. + * This should be limited to non-operating system related + * errors as possible. + * EX_OSERR -- An operating system error has been detected. + * This is intended to be used for such things as "cannot + * fork", "cannot create pipe", or the like. It includes + * things like getuid returning a user that does not + * exist in the passwd file. + * EX_OSFILE -- Some system file (e.g., /etc/passwd, /etc/utmp, + * etc.) does not exist, cannot be opened, or has some + * sort of error (e.g., syntax error). + * EX_CANTCREAT -- A (user specified) output file cannot be + * created. + * EX_IOERR -- An error occurred while doing I/O on some file. + * EX_TEMPFAIL -- temporary failure, indicating something that + * is not really an error. In sendmail, this means + * that a mailer (e.g.) could not create a connection, + * and the request should be reattempted later. + * EX_PROTOCOL -- the remote system returned something that + * was "not possible" during a protocol exchange. + * EX_NOPERM -- You did not have sufficient permission to + * perform the operation. This is not intended for + * file system problems, which should use NOINPUT or + * CANTCREAT, but rather for higher level permissions. + */ + +#define EX_OK 0 /* successful termination */ + +#define EX__BASE 64 /* base value for error messages */ + +#define EX_USAGE 64 /* command line usage error */ +#define EX_DATAERR 65 /* data format error */ +#define EX_NOINPUT 66 /* cannot open input */ +#define EX_NOUSER 67 /* addressee unknown */ +#define EX_NOHOST 68 /* host name unknown */ +#define EX_UNAVAILABLE 69 /* service unavailable */ +#define EX_SOFTWARE 70 /* internal software error */ +#define EX_OSERR 71 /* system error (e.g., can't fork) */ +#define EX_OSFILE 72 /* critical OS file missing */ +#define EX_CANTCREAT 73 /* can't create (user) output file */ +#define EX_IOERR 74 /* input/output error */ +#define EX_TEMPFAIL 75 /* temp failure; user is invited to retry */ +#define EX_PROTOCOL 76 /* remote error in protocol */ +#define EX_NOPERM 77 /* permission denied */ +#define EX_CONFIG 78 /* configuration error */ + +#define EX__MAX 78 /* maximum listed value */ + +#endif /* sysexits.h */ diff --git a/libraries/platforms/dromajo-vcs/software/linker/linker_gen.py b/libraries/platforms/dromajo-vcs/software/linker/linker_gen.py new file mode 100644 index 000000000..9ce248d4b --- /dev/null +++ b/libraries/platforms/dromajo-vcs/software/linker/linker_gen.py @@ -0,0 +1,196 @@ +# Copyright (c) 2020, University of Washington All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# Redistributions of source code must retain the above copyright notice, this list +# of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above copyright notice, this +# list of conditions and the following disclaimer in the documentation and/or +# other materials provided with the distribution. +# +# Neither the name of the copyright holder nor the names of its contributors may +# be used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#!/usr/bin/python +from __future__ import print_function + +import argparse + + +def print_linker(bp_dram_base, sp): + print( + """ + OUTPUT_ARCH( "riscv" ) + ENTRY(_start) + + SECTIONS {{ + + /*--------------------------------------------------------------------*/ + /* Code and read-only segment */ + /*--------------------------------------------------------------------*/ + + /* Begining of code and text segment */ + . = {bp_dram_base}; + _ftext = .; + PROVIDE( eprol = . ); + + /* text: Program code section */ + .text : + {{ + *(.text.init) + *(.text.emu) + *(.text.amo) + *(.text) + *(.text.*) + *(.gnu.linkonce.t.*) + }} + + /* init: Code to execute before main (called by crt0.S) */ + .init : + {{ + KEEP( *(.init) ) + }} + + /* fini: Code to execute after main (called by crt0.S) */ + .fini : + {{ + KEEP( *(.fini) ) + }} + + /* rodata: Read-only data */ + .rodata : + {{ + *(.rdata) + *(.rodata) + *(.rodata.*) + *(.gnu.linkonce.r.*) + }} + + /* End of code and read-only segment */ + PROVIDE( etext = . ); + _etext = .; + + /*--------------------------------------------------------------------*/ + /* Global constructor/destructor segement */ + /*--------------------------------------------------------------------*/ + + .preinit_array : + {{ + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + }} + + .init_array : + {{ + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array )) + PROVIDE_HIDDEN (__init_array_end = .); + }} + + .fini_array : + {{ + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT(.fini_array.*))) + KEEP (*(.fini_array )) + PROVIDE_HIDDEN (__fini_array_end = .); + }} + + .eh_frame_hdr : {{ *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) }} + .eh_frame : {{ KEEP (*(.eh_frame)) *(.eh_frame.*) }} + .gcc_except_table : {{ *(.gcc_except_table .gcc_except_table.*) }} + .gnu_extab : {{ *(.gnu_extab) }} + .exception_ranges : {{ *(.exception_ranges*) }} + .jcr : {{ KEEP (*(.jcr)) }} + + /*--------------------------------------------------------------------*/ + /* Initialized data segment */ + /*--------------------------------------------------------------------*/ + + /* Start of initialized data segment */ + . = ALIGN(16); + _fdata = .; + + /* data: Writable data */ + .data : + {{ + *(.data) + *(.data.*) + *(.gnu.linkonce.d.*) + }} + + /* 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.*) + }} + + /* End of initialized data segment */ + PROVIDE( edata = . ); + _edata = .; + + /*--------------------------------------------------------------------*/ + /* Uninitialized data segment */ + /*--------------------------------------------------------------------*/ + + /* Start of uninitialized data segment */ + . = ALIGN(8); + _fbss = .; + + /* Writable uninitialized small data segment */ + .sbss : + {{ + *(.sbss) + *(.sbss.*) + *(.gnu.linkonce.sb.*) + }} + + /* bss: Uninitialized writeable data section */ + . = .; + _bss_start = .; + .bss : + {{ + *(.bss) + *(.bss.*) + *(.gnu.linkonce.b.*) + *(COMMON) + }} + + /* End of uninitialized data segment (used by syscalls.c for heap) */ + PROVIDE( end = . ); + _end = ALIGN(8); + + _sp = {_sp}; + + }}""".format(bp_dram_base=bp_dram_base, _sp=sp) + ) + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("bp_dram_base", help="BlackParrot DRAM Base Address") + parser.add_argument("sp", help="Top of the stack") + args = parser.parse_args() + + print_linker(args.bp_dram_base, args.sp) \ No newline at end of file diff --git a/libraries/platforms/dromajo-vcs/software/src/argp/argp-ba.c b/libraries/platforms/dromajo-vcs/software/src/argp/argp-ba.c new file mode 100644 index 000000000..e083e7cee --- /dev/null +++ b/libraries/platforms/dromajo-vcs/software/src/argp/argp-ba.c @@ -0,0 +1,55 @@ +// Copyright (c) 2020, University of Washington All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// Redistributions of source code must retain the above copyright notice, this list +// of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above copyright notice, this +// list of conditions and the following disclaimer in the documentation and/or +// other materials provided with the distribution. +// +// Neither the name of the copyright holder nor the names of its contributors may +// be used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// This is a slightly modified copy of the argp library from Newlib + +/* Default definition for ARGP_PROGRAM_BUG_ADDRESS. + Copyright (C) 1996, 1997, 1999 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Written by Miles Bader . + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +/* If set by the user program, it should point to string that is the + bug-reporting address for the program. It will be printed by argp_help if + the ARGP_HELP_BUG_ADDR flag is set (as it is by various standard help + messages), embedded in a sentence that says something like `Report bugs to + ADDR.'. */ +const char *argp_program_bug_address; diff --git a/libraries/platforms/dromajo-vcs/software/src/argp/argp-eexst.c b/libraries/platforms/dromajo-vcs/software/src/argp/argp-eexst.c new file mode 100644 index 000000000..0e20f7ad7 --- /dev/null +++ b/libraries/platforms/dromajo-vcs/software/src/argp/argp-eexst.c @@ -0,0 +1,61 @@ +// Copyright (c) 2020, University of Washington All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// Redistributions of source code must retain the above copyright notice, this list +// of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above copyright notice, this +// list of conditions and the following disclaimer in the documentation and/or +// other materials provided with the distribution. +// +// Neither the name of the copyright holder nor the names of its contributors may +// be used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// This is a slightly modified copy of the argp library from Newlib + +/* Default definition for ARGP_ERR_EXIT_STATUS + Copyright (C) 1997 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Written by Miles Bader . + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include + +/* The exit status that argp will use when exiting due to a parsing error. + If not defined or set by the user program, this defaults to EX_USAGE from + . */ +error_t argp_err_exit_status = EX_USAGE; diff --git a/libraries/platforms/dromajo-vcs/software/src/argp/argp-fmtstream.c b/libraries/platforms/dromajo-vcs/software/src/argp/argp-fmtstream.c new file mode 100644 index 000000000..b4481f647 --- /dev/null +++ b/libraries/platforms/dromajo-vcs/software/src/argp/argp-fmtstream.c @@ -0,0 +1,458 @@ +// Copyright (c) 2020, University of Washington All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// Redistributions of source code must retain the above copyright notice, this list +// of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above copyright notice, this +// list of conditions and the following disclaimer in the documentation and/or +// other materials provided with the distribution. +// +// Neither the name of the copyright holder nor the names of its contributors may +// be used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// This is a slightly modified copy of the argp library from Newlib + +/* Word-wrapping and line-truncating streams + Copyright (C) 1997, 1998, 1999, 2001 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Written by Miles Bader . + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +/* This package emulates glibc `line_wrap_stream' semantics for systems that + don't have that. */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include "argp-fmtstream.h" +#include "argp-namefrob.h" + +#ifndef ARGP_FMTSTREAM_USE_LINEWRAP + +#ifndef isblank +#define isblank(ch) ((ch)==' ' || (ch)=='\t') +#endif + +#if defined _LIBC && defined USE_IN_LIBIO +# include +# include +# define __vsnprintf(s, l, f, a) _IO_vsnprintf (s, l, f, a) +#endif + +#define INIT_BUF_SIZE 200 +#define PRINTF_SIZE_GUESS 150 + +/* Return an argp_fmtstream that outputs to STREAM, and which prefixes lines + written on it with LMARGIN spaces and limits them to RMARGIN columns + total. If WMARGIN >= 0, words that extend past RMARGIN are wrapped by + replacing the whitespace before them with a newline and WMARGIN spaces. + Otherwise, chars beyond RMARGIN are simply dropped until a newline. + Returns NULL if there was an error. */ +argp_fmtstream_t +__argp_make_fmtstream (FILE *stream, size_t lmargin, size_t rmargin, ssize_t wmargin) +{ + argp_fmtstream_t fs; + + fs = (struct argp_fmtstream *) malloc (sizeof (struct argp_fmtstream)); + if (fs != NULL) + { + fs->stream = stream; + + fs->lmargin = lmargin; + fs->rmargin = rmargin; + fs->wmargin = wmargin; + fs->point_col = 0; + fs->point_offs = 0; + + fs->buf = (char *) malloc (INIT_BUF_SIZE); + if (! fs->buf) + { + free (fs); + fs = 0; + } + else + { + fs->p = fs->buf; + fs->end = fs->buf + INIT_BUF_SIZE; + } + } + + return fs; +} +#ifdef weak_alias +weak_alias (__argp_make_fmtstream, argp_make_fmtstream) +#endif + +/* Flush FS to its stream, and free it (but don't close the stream). */ +void +__argp_fmtstream_free (argp_fmtstream_t fs) +{ + __argp_fmtstream_update (fs); + if (fs->p > fs->buf) + { +#ifdef USE_IN_LIBIO + if (_IO_fwide (fs->stream, 0) > 0) + __fwprintf (fs->stream, L"%.*s", (int) (fs->p - fs->buf), fs->buf); + else +#endif + fwrite (fs->buf, 1, fs->p - fs->buf, fs->stream); + } + free (fs->buf); + free (fs); +} +#ifdef weak_alias +weak_alias (__argp_fmtstream_free, argp_fmtstream_free) +#endif + +/* Process FS's buffer so that line wrapping is done from POINT_OFFS to the + end of its buffer. This code is mostly from glibc stdio/linewrap.c. */ +void +__argp_fmtstream_update (argp_fmtstream_t fs) +{ + char *buf, *nl; + size_t len; + + /* Scan the buffer for newlines. */ + buf = fs->buf + fs->point_offs; + while (buf < fs->p) + { + size_t r; + + if (fs->point_col == 0 && fs->lmargin != 0) + { + /* We are starting a new line. Print spaces to the left margin. */ + const size_t pad = fs->lmargin; + if (fs->p + pad < fs->end) + { + /* We can fit in them in the buffer by moving the + buffer text up and filling in the beginning. */ + memmove (buf + pad, buf, fs->p - buf); + fs->p += pad; /* Compensate for bigger buffer. */ + memset (buf, ' ', pad); /* Fill in the spaces. */ + buf += pad; /* Don't bother searching them. */ + } + else + { + /* No buffer space for spaces. Must flush. */ + size_t i; + for (i = 0; i < pad; i++) + { +#ifdef USE_IN_LIBIO + if (_IO_fwide (fs->stream, 0) > 0) + putwc_unlocked (L' ', fs->stream); + else +#endif + putc_unlocked (' ', fs->stream); + } + } + fs->point_col = pad; + } + + len = fs->p - buf; + nl = memchr (buf, '\n', len); + + if (fs->point_col < 0) + fs->point_col = 0; + + if (!nl) + { + /* The buffer ends in a partial line. */ + + if (fs->point_col + len < fs->rmargin) + { + /* The remaining buffer text is a partial line and fits + within the maximum line width. Advance point for the + characters to be written and stop scanning. */ + fs->point_col += len; + break; + } + else + /* Set the end-of-line pointer for the code below to + the end of the buffer. */ + nl = fs->p; + } + else if (fs->point_col + (nl - buf) < (ssize_t) fs->rmargin) + { + /* The buffer contains a full line that fits within the maximum + line width. Reset point and scan the next line. */ + fs->point_col = 0; + buf = nl + 1; + continue; + } + + /* This line is too long. */ + r = fs->rmargin - 1; + + if (fs->wmargin < 0) + { + /* Truncate the line by overwriting the excess with the + newline and anything after it in the buffer. */ + if (nl < fs->p) + { + memmove (buf + (r - fs->point_col), nl, fs->p - nl); + fs->p -= buf + (r - fs->point_col) - nl; + /* Reset point for the next line and start scanning it. */ + fs->point_col = 0; + buf += r + 1; /* Skip full line plus \n. */ + } + else + { + /* The buffer ends with a partial line that is beyond the + maximum line width. Advance point for the characters + written, and discard those past the max from the buffer. */ + fs->point_col += len; + fs->p -= fs->point_col - r; + break; + } + } + else + { + /* Do word wrap. Go to the column just past the maximum line + width and scan back for the beginning of the word there. + Then insert a line break. */ + + char *p, *nextline; + int i; + + p = buf + (r + 1 - fs->point_col); + while (p >= buf && !isblank (*p)) + --p; + nextline = p + 1; /* This will begin the next line. */ + + if (nextline > buf) + { + /* Swallow separating blanks. */ + if (p >= buf) + do + --p; + while (p >= buf && isblank (*p)); + nl = p + 1; /* The newline will replace the first blank. */ + } + else + { + /* A single word that is greater than the maximum line width. + Oh well. Put it on an overlong line by itself. */ + p = buf + (r + 1 - fs->point_col); + /* Find the end of the long word. */ + do + ++p; + while (p < nl && !isblank (*p)); + if (p == nl) + { + /* It already ends a line. No fussing required. */ + fs->point_col = 0; + buf = nl + 1; + continue; + } + /* We will move the newline to replace the first blank. */ + nl = p; + /* Swallow separating blanks. */ + do + ++p; + while (isblank (*p)); + /* The next line will start here. */ + nextline = p; + } + + /* Note: There are a bunch of tests below for + NEXTLINE == BUF + LEN + 1; this case is where NL happens to fall + at the end of the buffer, and NEXTLINE is in fact empty (and so + we need not be careful to maintain its contents). */ + + if (nextline == buf + len + 1 + ? fs->end - nl < fs->wmargin + 1 + : nextline - (nl + 1) < fs->wmargin) + { + /* The margin needs more blanks than we removed. */ + if (fs->end - fs->p > fs->wmargin + 1) + /* Make some space for them. */ + { + size_t mv = fs->p - nextline; + memmove (nl + 1 + fs->wmargin, nextline, mv); + nextline = nl + 1 + fs->wmargin; + len = nextline + mv - buf; + *nl++ = '\n'; + } + else + /* Output the first line so we can use the space. */ + { +#ifdef USE_IN_LIBIO + if (_IO_fwide (fs->stream, 0) > 0) + __fwprintf (fs->stream, L"%.*s\n", + (int) (nl - fs->buf), fs->buf); + else +#endif + { + if (nl > fs->buf) + fwrite (fs->buf, 1, nl - fs->buf, fs->stream); + putc_unlocked ('\n', fs->stream); + } + len += buf - fs->buf; + nl = buf = fs->buf; + } + } + else + /* We can fit the newline and blanks in before + the next word. */ + *nl++ = '\n'; + + if (nextline - nl >= fs->wmargin + || (nextline == buf + len + 1 && fs->end - nextline >= fs->wmargin)) + /* Add blanks up to the wrap margin column. */ + for (i = 0; i < fs->wmargin; ++i) + *nl++ = ' '; + else + for (i = 0; i < fs->wmargin; ++i) +#ifdef USE_IN_LIBIO + if (_IO_fwide (fs->stream, 0) > 0) + putwc_unlocked (L' ', fs->stream); + else +#endif + putc_unlocked (' ', fs->stream); + + /* Copy the tail of the original buffer into the current buffer + position. */ + if (nl < nextline) + memmove (nl, nextline, buf + len - nextline); + len -= nextline - buf; + + /* Continue the scan on the remaining lines in the buffer. */ + buf = nl; + + /* Restore bufp to include all the remaining text. */ + fs->p = nl + len; + + /* Reset the counter of what has been output this line. If wmargin + is 0, we want to avoid the lmargin getting added, so we set + point_col to a magic value of -1 in that case. */ + fs->point_col = fs->wmargin ? fs->wmargin : -1; + } + } + + /* Remember that we've scanned as far as the end of the buffer. */ + fs->point_offs = fs->p - fs->buf; +} + +/* Ensure that FS has space for AMOUNT more bytes in its buffer, either by + growing the buffer, or by flushing it. True is returned iff we succeed. */ +int +__argp_fmtstream_ensure (struct argp_fmtstream *fs, size_t amount) +{ + if ((size_t) (fs->end - fs->p) < amount) + { + ssize_t wrote; + + /* Flush FS's buffer. */ + __argp_fmtstream_update (fs); + +#ifdef USE_IN_LIBIO + if (_IO_fwide (fs->stream, 0) > 0) + { + __fwprintf (fs->stream, L"%.*s", (int) (fs->p - fs->buf), fs->buf); + wrote = fs->p - fs->buf; + } + else +#endif + wrote = fwrite (fs->buf, 1, fs->p - fs->buf, fs->stream); + if (wrote == fs->p - fs->buf) + { + fs->p = fs->buf; + fs->point_offs = 0; + } + else + { + fs->p -= wrote; + fs->point_offs -= wrote; + memmove (fs->buf, fs->buf + wrote, fs->p - fs->buf); + return 0; + } + + if ((size_t) (fs->end - fs->buf) < amount) + /* Gotta grow the buffer. */ + { + size_t new_size = fs->end - fs->buf + amount; + char *new_buf = realloc (fs->buf, new_size); + + if (! new_buf) + { + __set_errno (ENOMEM); + return 0; + } + + fs->buf = new_buf; + fs->end = new_buf + new_size; + fs->p = fs->buf; + } + } + + return 1; +} + +ssize_t +__argp_fmtstream_printf (struct argp_fmtstream *fs, const char *fmt, ...) +{ + int out; + size_t avail; + size_t size_guess = PRINTF_SIZE_GUESS; /* How much space to reserve. */ + + do + { + va_list args; + + if (! __argp_fmtstream_ensure (fs, size_guess)) + return -1; + + va_start (args, fmt); + avail = fs->end - fs->p; + out = __vsnprintf (fs->p, avail, fmt, args); + va_end (args); + if (out >= avail) + size_guess = out + 1; + } + while (out >= avail); + + fs->p += out; + + return out; +} +#ifdef weak_alias +weak_alias (__argp_fmtstream_printf, argp_fmtstream_printf) +#endif + +#endif /* !ARGP_FMTSTREAM_USE_LINEWRAP */ diff --git a/libraries/platforms/dromajo-vcs/software/src/argp/argp-fs-xinl.c b/libraries/platforms/dromajo-vcs/software/src/argp/argp-fs-xinl.c new file mode 100644 index 000000000..910401859 --- /dev/null +++ b/libraries/platforms/dromajo-vcs/software/src/argp/argp-fs-xinl.c @@ -0,0 +1,70 @@ +// Copyright (c) 2020, University of Washington All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// Redistributions of source code must retain the above copyright notice, this list +// of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above copyright notice, this +// list of conditions and the following disclaimer in the documentation and/or +// other materials provided with the distribution. +// +// Neither the name of the copyright holder nor the names of its contributors may +// be used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// This is a slightly modified copy of the argp library from Newlib + +/* Real definitions for extern inline functions in argp-fmtstream.h + Copyright (C) 1997 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Written by Miles Bader . + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#define ARGP_FS_EI +#undef __OPTIMIZE__ +#define __OPTIMIZE__ 1 +#include "argp-fmtstream.h" + +/* Add weak aliases. */ +#if _LIBC - 0 && !defined (ARGP_FMTSTREAM_USE_LINEWRAP) && defined (weak_alias) + +weak_alias (__argp_fmtstream_putc, argp_fmtstream_putc) +weak_alias (__argp_fmtstream_puts, argp_fmtstream_puts) +weak_alias (__argp_fmtstream_write, argp_fmtstream_write) +weak_alias (__argp_fmtstream_set_lmargin, argp_fmtstream_set_lmargin) +weak_alias (__argp_fmtstream_set_rmargin, argp_fmtstream_set_rmargin) +weak_alias (__argp_fmtstream_set_wmargin, argp_fmtstream_set_wmargin) +weak_alias (__argp_fmtstream_point, argp_fmtstream_point) + +#endif diff --git a/libraries/platforms/dromajo-vcs/software/src/argp/argp-help.c b/libraries/platforms/dromajo-vcs/software/src/argp/argp-help.c new file mode 100644 index 000000000..839aa1850 --- /dev/null +++ b/libraries/platforms/dromajo-vcs/software/src/argp/argp-help.c @@ -0,0 +1,1891 @@ +// Copyright (c) 2020, University of Washington All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// Redistributions of source code must retain the above copyright notice, this list +// of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above copyright notice, this +// list of conditions and the following disclaimer in the documentation and/or +// other materials provided with the distribution. +// +// Neither the name of the copyright holder nor the names of its contributors may +// be used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// This is a slightly modified copy of the argp library from Newlib + +/* Hierarchial argument parsing help output + Copyright (C) 1995-2000, 2001 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Written by Miles Bader . + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +#endif + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef alloca +# ifdef __GNUC__ +# define alloca __builtin_alloca +# define HAVE_ALLOCA 1 +# else +# if defined HAVE_ALLOCA_H || defined _LIBC +# include +# else +# ifdef _AIX + #pragma alloca +# else +# ifndef alloca +char *alloca (); +# endif +# endif +# endif +# endif +#endif + +#include +#include +#include +#include +#include +#include +#include +#ifdef USE_IN_LIBIO +# include +#endif + +#ifndef _ +/* This is for other GNU distributions with internationalized messages. */ +# if defined HAVE_LIBINTL_H || defined _LIBC +# include +# ifdef _LIBC +# undef dgettext +# define dgettext(domain, msgid) __dcgettext (domain, msgid, LC_MESSAGES) +# endif +# else +# define dgettext(domain, msgid) (msgid) +# endif +#endif + +#include +#include +#include + +/* User-selectable (using an environment variable) formatting parameters. + + These may be specified in an environment variable called `ARGP_HELP_FMT', + with a contents like: VAR1=VAL1,VAR2=VAL2,BOOLVAR2,no-BOOLVAR2 + Where VALn must be a positive integer. The list of variables is in the + UPARAM_NAMES vector, below. */ + +/* Default parameters. */ +#define DUP_ARGS 0 /* True if option argument can be duplicated. */ +#define DUP_ARGS_NOTE 1 /* True to print a note about duplicate args. */ +#define SHORT_OPT_COL 2 /* column in which short options start */ +#define LONG_OPT_COL 6 /* column in which long options start */ +#define DOC_OPT_COL 2 /* column in which doc options start */ +#define OPT_DOC_COL 29 /* column in which option text starts */ +#define HEADER_COL 1 /* column in which group headers are printed */ +#define USAGE_INDENT 12 /* indentation of wrapped usage lines */ +#define RMARGIN 79 /* right margin used for wrapping */ + +/* User-selectable (using an environment variable) formatting parameters. + They must all be of type `int' for the parsing code to work. */ +struct uparams +{ + /* If true, arguments for an option are shown with both short and long + options, even when a given option has both, e.g. `-x ARG, --longx=ARG'. + If false, then if an option has both, the argument is only shown with + the long one, e.g., `-x, --longx=ARG', and a message indicating that + this really means both is printed below the options. */ + int dup_args; + + /* This is true if when DUP_ARGS is false, and some duplicate arguments have + been suppressed, an explanatory message should be printed. */ + int dup_args_note; + + /* Various output columns. */ + int short_opt_col; + int long_opt_col; + int doc_opt_col; + int opt_doc_col; + int header_col; + int usage_indent; + int rmargin; + + int valid; /* True when the values in here are valid. */ +}; + +/* This is a global variable, as user options are only ever read once. */ +static struct uparams uparams = { + DUP_ARGS, DUP_ARGS_NOTE, + SHORT_OPT_COL, LONG_OPT_COL, DOC_OPT_COL, OPT_DOC_COL, HEADER_COL, + USAGE_INDENT, RMARGIN, + 0 +}; + +/* A particular uparam, and what the user name is. */ +struct uparam_name +{ + const char *name; /* User name. */ + int is_bool; /* Whether it's `boolean'. */ + size_t uparams_offs; /* Location of the (int) field in UPARAMS. */ +}; + +/* The name-field mappings we know about. */ +static const struct uparam_name uparam_names[] = +{ + { "dup-args", 1, offsetof (struct uparams, dup_args) }, + { "dup-args-note", 1, offsetof (struct uparams, dup_args_note) }, + { "short-opt-col", 0, offsetof (struct uparams, short_opt_col) }, + { "long-opt-col", 0, offsetof (struct uparams, long_opt_col) }, + { "doc-opt-col", 0, offsetof (struct uparams, doc_opt_col) }, + { "opt-doc-col", 0, offsetof (struct uparams, opt_doc_col) }, + { "header-col", 0, offsetof (struct uparams, header_col) }, + { "usage-indent", 0, offsetof (struct uparams, usage_indent) }, + { "rmargin", 0, offsetof (struct uparams, rmargin) }, + { 0 } +}; + +/* Read user options from the environment, and fill in UPARAMS appropiately. */ +static void +fill_in_uparams (const struct argp_state *state) +{ + const char *var = getenv ("ARGP_HELP_FMT"); + +#define SKIPWS(p) do { while (isspace (*p)) p++; } while (0); + + if (var) + /* Parse var. */ + while (*var) + { + SKIPWS (var); + + if (isalpha (*var)) + { + size_t var_len; + const struct uparam_name *un; + int unspec = 0, val = 0; + const char *arg = var; + + while (isalnum (*arg) || *arg == '-' || *arg == '_') + arg++; + var_len = arg - var; + + SKIPWS (arg); + + if (*arg == '\0' || *arg == ',') + unspec = 1; + else if (*arg == '=') + { + arg++; + SKIPWS (arg); + } + + if (unspec) + { + if (var[0] == 'n' && var[1] == 'o' && var[2] == '-') + { + val = 0; + var += 3; + var_len -= 3; + } + else + val = 1; + } + else if (isdigit (*arg)) + { + val = atoi (arg); + while (isdigit (*arg)) + arg++; + SKIPWS (arg); + } + + for (un = uparam_names; un->name; un++) + if (strlen (un->name) == var_len + && strncmp (var, un->name, var_len) == 0) + { + if (unspec && !un->is_bool) + __argp_failure (state, 0, 0, + dgettext (state->root_argp->argp_domain, "\ + %.*s: ARGP_HELP_FMT parameter requires a value"), + (int) var_len, var); + else + *(int *)((char *)&uparams + un->uparams_offs) = val; + break; + } + if (! un->name) + __argp_failure (state, 0, 0, + dgettext (state->root_argp->argp_domain, "\ + %.*s: Unknown ARGP_HELP_FMT parameter"), + (int) var_len, var); + + var = arg; + if (*var == ',') + var++; + } + else if (*var) + { + __argp_failure (state, 0, 0, + dgettext (state->root_argp->argp_domain, + "Garbage in ARGP_HELP_FMT: %s"), var); + break; + } + } +} + +/* Returns true if OPT hasn't been marked invisible. Visibility only affects + whether OPT is displayed or used in sorting, not option shadowing. */ +#define ovisible(opt) (! ((opt)->flags & OPTION_HIDDEN)) + +/* Returns true if OPT is an alias for an earlier option. */ +#define oalias(opt) ((opt)->flags & OPTION_ALIAS) + +/* Returns true if OPT is an documentation-only entry. */ +#define odoc(opt) ((opt)->flags & OPTION_DOC) + +/* Returns true if OPT is the end-of-list marker for a list of options. */ +#define oend(opt) __option_is_end (opt) + +/* Returns true if OPT has a short option. */ +#define oshort(opt) __option_is_short (opt) + +/* + The help format for a particular option is like: + + -xARG, -yARG, --long1=ARG, --long2=ARG Documentation... + + Where ARG will be omitted if there's no argument, for this option, or + will be surrounded by "[" and "]" appropiately if the argument is + optional. The documentation string is word-wrapped appropiately, and if + the list of options is long enough, it will be started on a separate line. + If there are no short options for a given option, the first long option is + indented slighly in a way that's supposed to make most long options appear + to be in a separate column. + + For example, the following output (from ps): + + -p PID, --pid=PID List the process PID + --pgrp=PGRP List processes in the process group PGRP + -P, -x, --no-parent Include processes without parents + -Q, --all-fields Don't elide unusable fields (normally if there's + some reason ps can't print a field for any + process, it's removed from the output entirely) + -r, --reverse, --gratuitously-long-reverse-option + Reverse the order of any sort + --session[=SID] Add the processes from the session SID (which + defaults to the sid of the current process) + + Here are some more options: + -f ZOT, --foonly=ZOT Glork a foonly + -z, --zaza Snit a zar + + -?, --help Give this help list + --usage Give a short usage message + -V, --version Print program version + + The struct argp_option array for the above could look like: + + { + {"pid", 'p', "PID", 0, "List the process PID"}, + {"pgrp", OPT_PGRP, "PGRP", 0, "List processes in the process group PGRP"}, + {"no-parent", 'P', 0, 0, "Include processes without parents"}, + {0, 'x', 0, OPTION_ALIAS}, + {"all-fields",'Q', 0, 0, "Don't elide unusable fields (normally" + " if there's some reason ps can't" + " print a field for any process, it's" + " removed from the output entirely)" }, + {"reverse", 'r', 0, 0, "Reverse the order of any sort"}, + {"gratuitously-long-reverse-option", 0, 0, OPTION_ALIAS}, + {"session", OPT_SESS, "SID", OPTION_ARG_OPTIONAL, + "Add the processes from the session" + " SID (which defaults to the sid of" + " the current process)" }, + + {0,0,0,0, "Here are some more options:"}, + {"foonly", 'f', "ZOT", 0, "Glork a foonly"}, + {"zaza", 'z', 0, 0, "Snit a zar"}, + + {0} + } + + Note that the last three options are automatically supplied by argp_parse, + unless you tell it not to with ARGP_NO_HELP. + +*/ + +/* Returns true if CH occurs between BEG and END. */ +static int +find_char (char ch, char *beg, char *end) +{ + while (beg < end) + if (*beg == ch) + return 1; + else + beg++; + return 0; +} + +struct hol_cluster; /* fwd decl */ + +struct hol_entry +{ + /* First option. */ + const struct argp_option *opt; + /* Number of options (including aliases). */ + unsigned num; + + /* A pointers into the HOL's short_options field, to the first short option + letter for this entry. The order of the characters following this point + corresponds to the order of options pointed to by OPT, and there are at + most NUM. A short option recorded in a option following OPT is only + valid if it occurs in the right place in SHORT_OPTIONS (otherwise it's + probably been shadowed by some other entry). */ + char *short_options; + + /* Entries are sorted by their group first, in the order: + 1, 2, ..., n, 0, -m, ..., -2, -1 + and then alphabetically within each group. The default is 0. */ + int group; + + /* The cluster of options this entry belongs to, or 0 if none. */ + struct hol_cluster *cluster; + + /* The argp from which this option came. */ + const struct argp *argp; +}; + +/* A cluster of entries to reflect the argp tree structure. */ +struct hol_cluster +{ + /* A descriptive header printed before options in this cluster. */ + const char *header; + + /* Used to order clusters within the same group with the same parent, + according to the order in which they occurred in the parent argp's child + list. */ + int index; + + /* How to sort this cluster with respect to options and other clusters at the + same depth (clusters always follow options in the same group). */ + int group; + + /* The cluster to which this cluster belongs, or 0 if it's at the base + level. */ + struct hol_cluster *parent; + + /* The argp from which this cluster is (eventually) derived. */ + const struct argp *argp; + + /* The distance this cluster is from the root. */ + int depth; + + /* Clusters in a given hol are kept in a linked list, to make freeing them + possible. */ + struct hol_cluster *next; +}; + +/* A list of options for help. */ +struct hol +{ + /* An array of hol_entry's. */ + struct hol_entry *entries; + /* The number of entries in this hol. If this field is zero, the others + are undefined. */ + unsigned num_entries; + + /* A string containing all short options in this HOL. Each entry contains + pointers into this string, so the order can't be messed with blindly. */ + char *short_options; + + /* Clusters of entries in this hol. */ + struct hol_cluster *clusters; +}; + +/* Create a struct hol from the options in ARGP. CLUSTER is the + hol_cluster in which these entries occur, or 0, if at the root. */ +static struct hol * +make_hol (const struct argp *argp, struct hol_cluster *cluster) +{ + char *so; + const struct argp_option *o; + const struct argp_option *opts = argp->options; + struct hol_entry *entry; + unsigned num_short_options = 0; + struct hol *hol = malloc (sizeof (struct hol)); + + assert (hol); + + hol->num_entries = 0; + hol->clusters = 0; + + if (opts) + { + int cur_group = 0; + + /* The first option must not be an alias. */ + assert (! oalias (opts)); + + /* Calculate the space needed. */ + for (o = opts; ! oend (o); o++) + { + if (! oalias (o)) + hol->num_entries++; + if (oshort (o)) + num_short_options++; /* This is an upper bound. */ + } + + hol->entries = malloc (sizeof (struct hol_entry) * hol->num_entries); + hol->short_options = malloc (num_short_options + 1); + + assert (hol->entries && hol->short_options); + + /* Fill in the entries. */ + so = hol->short_options; + for (o = opts, entry = hol->entries; ! oend (o); entry++) + { + entry->opt = o; + entry->num = 0; + entry->short_options = so; + entry->group = cur_group = + o->group + ? o->group + : ((!o->name && !o->key) + ? cur_group + 1 + : cur_group); + entry->cluster = cluster; + entry->argp = argp; + + do + { + entry->num++; + if (oshort (o) && ! find_char (o->key, hol->short_options, so)) + /* O has a valid short option which hasn't already been used.*/ + *so++ = o->key; + o++; + } + while (! oend (o) && oalias (o)); + } + *so = '\0'; /* null terminated so we can find the length */ + } + + return hol; +} + +/* Add a new cluster to HOL, with the given GROUP and HEADER (taken from the + associated argp child list entry), INDEX, and PARENT, and return a pointer + to it. ARGP is the argp that this cluster results from. */ +static struct hol_cluster * +hol_add_cluster (struct hol *hol, int group, const char *header, int index, + struct hol_cluster *parent, const struct argp *argp) +{ + struct hol_cluster *cl = malloc (sizeof (struct hol_cluster)); + if (cl) + { + cl->group = group; + cl->header = header; + + cl->index = index; + cl->parent = parent; + cl->argp = argp; + cl->depth = parent ? parent->depth + 1 : 0; + + cl->next = hol->clusters; + hol->clusters = cl; + } + return cl; +} + +/* Free HOL and any resources it uses. */ +static void +hol_free (struct hol *hol) +{ + struct hol_cluster *cl = hol->clusters; + + while (cl) + { + struct hol_cluster *next = cl->next; + free (cl); + cl = next; + } + + if (hol->num_entries > 0) + { + free (hol->entries); + free (hol->short_options); + } + + free (hol); +} + +static inline int +hol_entry_short_iterate (const struct hol_entry *entry, + int (*func)(const struct argp_option *opt, + const struct argp_option *real, + const char *domain, void *cookie), + const char *domain, void *cookie) +{ + unsigned nopts; + int val = 0; + const struct argp_option *opt, *real = entry->opt; + char *so = entry->short_options; + + for (opt = real, nopts = entry->num; nopts > 0 && !val; opt++, nopts--) + if (oshort (opt) && *so == opt->key) + { + if (!oalias (opt)) + real = opt; + if (ovisible (opt)) + val = (*func)(opt, real, domain, cookie); + so++; + } + + return val; +} + +static inline int +hol_entry_long_iterate (const struct hol_entry *entry, + int (*func)(const struct argp_option *opt, + const struct argp_option *real, + const char *domain, void *cookie), + const char *domain, void *cookie) +{ + unsigned nopts; + int val = 0; + const struct argp_option *opt, *real = entry->opt; + + for (opt = real, nopts = entry->num; nopts > 0 && !val; opt++, nopts--) + if (opt->name) + { + if (!oalias (opt)) + real = opt; + if (ovisible (opt)) + val = (*func)(opt, real, domain, cookie); + } + + return val; +} + +/* Iterator that returns true for the first short option. */ +static inline int +until_short (const struct argp_option *opt, const struct argp_option *real, + const char *domain, void *cookie) +{ + return oshort (opt) ? opt->key : 0; +} + +/* Returns the first valid short option in ENTRY, or 0 if there is none. */ +static char +hol_entry_first_short (const struct hol_entry *entry) +{ + return hol_entry_short_iterate (entry, until_short, + entry->argp->argp_domain, 0); +} + +/* Returns the first valid long option in ENTRY, or 0 if there is none. */ +static const char * +hol_entry_first_long (const struct hol_entry *entry) +{ + const struct argp_option *opt; + unsigned num; + for (opt = entry->opt, num = entry->num; num > 0; opt++, num--) + if (opt->name && ovisible (opt)) + return opt->name; + return 0; +} + +/* Returns the entry in HOL with the long option name NAME, or 0 if there is + none. */ +static struct hol_entry * +hol_find_entry (struct hol *hol, const char *name) +{ + struct hol_entry *entry = hol->entries; + unsigned num_entries = hol->num_entries; + + while (num_entries-- > 0) + { + const struct argp_option *opt = entry->opt; + unsigned num_opts = entry->num; + + while (num_opts-- > 0) + if (opt->name && ovisible (opt) && strcmp (opt->name, name) == 0) + return entry; + else + opt++; + + entry++; + } + + return 0; +} + +/* If an entry with the long option NAME occurs in HOL, set it's special + sort position to GROUP. */ +static void +hol_set_group (struct hol *hol, const char *name, int group) +{ + struct hol_entry *entry = hol_find_entry (hol, name); + if (entry) + entry->group = group; +} + +/* Order by group: 0, 1, 2, ..., n, -m, ..., -2, -1. + EQ is what to return if GROUP1 and GROUP2 are the same. */ +static int +group_cmp (int group1, int group2, int eq) +{ + if (group1 == group2) + return eq; + else if ((group1 < 0 && group2 < 0) || (group1 >= 0 && group2 >= 0)) + return group1 - group2; + else + return group2 - group1; +} + +/* Compare clusters CL1 & CL2 by the order that they should appear in + output. */ +static int +hol_cluster_cmp (const struct hol_cluster *cl1, const struct hol_cluster *cl2) +{ + /* If one cluster is deeper than the other, use its ancestor at the same + level, so that finding the common ancestor is straightforward. */ + while (cl1->depth < cl2->depth) + cl1 = cl1->parent; + while (cl2->depth < cl1->depth) + cl2 = cl2->parent; + + /* Now reduce both clusters to their ancestors at the point where both have + a common parent; these can be directly compared. */ + while (cl1->parent != cl2->parent) + cl1 = cl1->parent, cl2 = cl2->parent; + + return group_cmp (cl1->group, cl2->group, cl2->index - cl1->index); +} + +/* Return the ancestor of CL that's just below the root (i.e., has a parent + of 0). */ +static struct hol_cluster * +hol_cluster_base (struct hol_cluster *cl) +{ + while (cl->parent) + cl = cl->parent; + return cl; +} + +/* Return true if CL1 is a child of CL2. */ +static int +hol_cluster_is_child (const struct hol_cluster *cl1, + const struct hol_cluster *cl2) +{ + while (cl1 && cl1 != cl2) + cl1 = cl1->parent; + return cl1 == cl2; +} + +/* Given the name of a OPTION_DOC option, modifies NAME to start at the tail + that should be used for comparisons, and returns true iff it should be + treated as a non-option. */ +static int +canon_doc_option (const char **name) +{ + int non_opt; + /* Skip initial whitespace. */ + while (isspace (**name)) + (*name)++; + /* Decide whether this looks like an option (leading `-') or not. */ + non_opt = (**name != '-'); + /* Skip until part of name used for sorting. */ + while (**name && !isalnum (**name)) + (*name)++; + return non_opt; +} + +/* Order ENTRY1 & ENTRY2 by the order which they should appear in a help + listing. */ +static int +hol_entry_cmp (const struct hol_entry *entry1, + const struct hol_entry *entry2) +{ + /* The group numbers by which the entries should be ordered; if either is + in a cluster, then this is just the group within the cluster. */ + int group1 = entry1->group, group2 = entry2->group; + + if (entry1->cluster != entry2->cluster) + { + /* The entries are not within the same cluster, so we can't compare them + directly, we have to use the appropiate clustering level too. */ + if (! entry1->cluster) + /* ENTRY1 is at the `base level', not in a cluster, so we have to + compare it's group number with that of the base cluster in which + ENTRY2 resides. Note that if they're in the same group, the + clustered option always comes laster. */ + return group_cmp (group1, hol_cluster_base (entry2->cluster)->group, -1); + else if (! entry2->cluster) + /* Likewise, but ENTRY2's not in a cluster. */ + return group_cmp (hol_cluster_base (entry1->cluster)->group, group2, 1); + else + /* Both entries are in clusters, we can just compare the clusters. */ + return hol_cluster_cmp (entry1->cluster, entry2->cluster); + } + else if (group1 == group2) + /* The entries are both in the same cluster and group, so compare them + alphabetically. */ + { + int short1 = hol_entry_first_short (entry1); + int short2 = hol_entry_first_short (entry2); + int doc1 = odoc (entry1->opt); + int doc2 = odoc (entry2->opt); + const char *long1 = hol_entry_first_long (entry1); + const char *long2 = hol_entry_first_long (entry2); + + if (doc1) + doc1 = canon_doc_option (&long1); + if (doc2) + doc2 = canon_doc_option (&long2); + + if (doc1 != doc2) + /* `documentation' options always follow normal options (or + documentation options that *look* like normal options). */ + return doc1 - doc2; + else if (!short1 && !short2 && long1 && long2) + /* Only long options. */ + return __strcasecmp (long1, long2); + else + /* Compare short/short, long/short, short/long, using the first + character of long options. Entries without *any* valid + options (such as options with OPTION_HIDDEN set) will be put + first, but as they're not displayed, it doesn't matter where + they are. */ + { + char first1 = short1 ? short1 : long1 ? *long1 : 0; + char first2 = short2 ? short2 : long2 ? *long2 : 0; +#ifdef _tolower + int lower_cmp = _tolower (first1) - _tolower (first2); +#else + int lower_cmp = tolower (first1) - tolower (first2); +#endif + /* Compare ignoring case, except when the options are both the + same letter, in which case lower-case always comes first. */ + return lower_cmp ? lower_cmp : first2 - first1; + } + } + else + /* Within the same cluster, but not the same group, so just compare + groups. */ + return group_cmp (group1, group2, 0); +} + +/* Version of hol_entry_cmp with correct signature for qsort. */ +static int +hol_entry_qcmp (const void *entry1_v, const void *entry2_v) +{ + return hol_entry_cmp (entry1_v, entry2_v); +} + +/* Sort HOL by group and alphabetically by option name (with short options + taking precedence over long). Since the sorting is for display purposes + only, the shadowing of options isn't effected. */ +static void +hol_sort (struct hol *hol) +{ + if (hol->num_entries > 0) + qsort (hol->entries, hol->num_entries, sizeof (struct hol_entry), + hol_entry_qcmp); +} + +/* Append MORE to HOL, destroying MORE in the process. Options in HOL shadow + any in MORE with the same name. */ +static void +hol_append (struct hol *hol, struct hol *more) +{ + struct hol_cluster **cl_end = &hol->clusters; + char *tmp; + + /* Steal MORE's cluster list, and add it to the end of HOL's. */ + while (*cl_end) + cl_end = &(*cl_end)->next; + *cl_end = more->clusters; + more->clusters = 0; + + /* Merge entries. */ + if (more->num_entries > 0) + { + if (hol->num_entries == 0) + { + hol->num_entries = more->num_entries; + hol->entries = more->entries; + hol->short_options = more->short_options; + more->num_entries = 0; /* Mark MORE's fields as invalid. */ + } + else + /* Append the entries in MORE to those in HOL, taking care to only add + non-shadowed SHORT_OPTIONS values. */ + { + unsigned left; + char *so, *more_so; + struct hol_entry *e; + unsigned num_entries = hol->num_entries + more->num_entries; + struct hol_entry *entries = + malloc (num_entries * sizeof (struct hol_entry)); + unsigned hol_so_len = strlen (hol->short_options); + char *short_options = + malloc (hol_so_len + strlen (more->short_options) + 1); + + tmp = memcpy (entries, hol->entries, + hol->num_entries * sizeof (struct hol_entry)); + tmp += hol->num_entries * sizeof (struct hol_entry); + memcpy (tmp, + more->entries, + more->num_entries * sizeof (struct hol_entry)); + + memcpy (short_options, hol->short_options, hol_so_len); + + /* Fix up the short options pointers from HOL. */ + for (e = entries, left = hol->num_entries; left > 0; e++, left--) + e->short_options += (short_options - hol->short_options); + + /* Now add the short options from MORE, fixing up its entries + too. */ + so = short_options + hol_so_len; + more_so = more->short_options; + for (left = more->num_entries; left > 0; e++, left--) + { + int opts_left; + const struct argp_option *opt; + + e->short_options = so; + + for (opts_left = e->num, opt = e->opt; opts_left; opt++, opts_left--) + { + int ch = *more_so; + if (oshort (opt) && ch == opt->key) + /* The next short option in MORE_SO, CH, is from OPT. */ + { + if (! find_char (ch, short_options, + short_options + hol_so_len)) + /* The short option CH isn't shadowed by HOL's options, + so add it to the sum. */ + *so++ = ch; + more_so++; + } + } + } + + *so = '\0'; + + free (hol->entries); + free (hol->short_options); + + hol->entries = entries; + hol->num_entries = num_entries; + hol->short_options = short_options; + } + } + + hol_free (more); +} + +/* Inserts enough spaces to make sure STREAM is at column COL. */ +static void +indent_to (argp_fmtstream_t stream, unsigned col) +{ + int needed = col - __argp_fmtstream_point (stream); + while (needed-- > 0) + __argp_fmtstream_putc (stream, ' '); +} + +/* Output to STREAM either a space, or a newline if there isn't room for at + least ENSURE characters before the right margin. */ +static void +space (argp_fmtstream_t stream, size_t ensure) +{ + if (__argp_fmtstream_point (stream) + ensure + >= __argp_fmtstream_rmargin (stream)) + __argp_fmtstream_putc (stream, '\n'); + else + __argp_fmtstream_putc (stream, ' '); +} + +/* If the option REAL has an argument, we print it in using the printf + format REQ_FMT or OPT_FMT depending on whether it's a required or + optional argument. */ +static void +arg (const struct argp_option *real, const char *req_fmt, const char *opt_fmt, + const char *domain, argp_fmtstream_t stream) +{ + if (real->arg) + { + if (real->flags & OPTION_ARG_OPTIONAL) + __argp_fmtstream_printf (stream, opt_fmt, + dgettext (domain, real->arg)); + else + __argp_fmtstream_printf (stream, req_fmt, + dgettext (domain, real->arg)); + } +} + +/* Helper functions for hol_entry_help. */ + +/* State used during the execution of hol_help. */ +struct hol_help_state +{ + /* PREV_ENTRY should contain the previous entry printed, or 0. */ + struct hol_entry *prev_entry; + + /* If an entry is in a different group from the previous one, and SEP_GROUPS + is true, then a blank line will be printed before any output. */ + int sep_groups; + + /* True if a duplicate option argument was suppressed (only ever set if + UPARAMS.dup_args is false). */ + int suppressed_dup_arg; +}; + +/* Some state used while printing a help entry (used to communicate with + helper functions). See the doc for hol_entry_help for more info, as most + of the fields are copied from its arguments. */ +struct pentry_state +{ + const struct hol_entry *entry; + argp_fmtstream_t stream; + struct hol_help_state *hhstate; + + /* True if nothing's been printed so far. */ + int first; + + /* If non-zero, the state that was used to print this help. */ + const struct argp_state *state; +}; + +/* If a user doc filter should be applied to DOC, do so. */ +static const char * +filter_doc (const char *doc, int key, const struct argp *argp, + const struct argp_state *state) +{ + if (argp->help_filter) + /* We must apply a user filter to this output. */ + { + void *input = __argp_input (argp, state); + return (*argp->help_filter) (key, doc, input); + } + else + /* No filter. */ + return doc; +} + +/* Prints STR as a header line, with the margin lines set appropiately, and + notes the fact that groups should be separated with a blank line. ARGP is + the argp that should dictate any user doc filtering to take place. Note + that the previous wrap margin isn't restored, but the left margin is reset + to 0. */ +static void +print_header (const char *str, const struct argp *argp, + struct pentry_state *pest) +{ + const char *tstr = dgettext (argp->argp_domain, str); + const char *fstr = filter_doc (tstr, ARGP_KEY_HELP_HEADER, argp, pest->state); + + if (fstr) + { + if (*fstr) + { + if (pest->hhstate->prev_entry) + /* Precede with a blank line. */ + __argp_fmtstream_putc (pest->stream, '\n'); + indent_to (pest->stream, uparams.header_col); + __argp_fmtstream_set_lmargin (pest->stream, uparams.header_col); + __argp_fmtstream_set_wmargin (pest->stream, uparams.header_col); + __argp_fmtstream_puts (pest->stream, fstr); + __argp_fmtstream_set_lmargin (pest->stream, 0); + __argp_fmtstream_putc (pest->stream, '\n'); + } + + pest->hhstate->sep_groups = 1; /* Separate subsequent groups. */ + } + + if (fstr != tstr) + free ((char *) fstr); +} + +/* Inserts a comma if this isn't the first item on the line, and then makes + sure we're at least to column COL. If this *is* the first item on a line, + prints any pending whitespace/headers that should precede this line. Also + clears FIRST. */ +static void +comma (unsigned col, struct pentry_state *pest) +{ + if (pest->first) + { + const struct hol_entry *pe = pest->hhstate->prev_entry; + const struct hol_cluster *cl = pest->entry->cluster; + + if (pest->hhstate->sep_groups && pe && pest->entry->group != pe->group) + __argp_fmtstream_putc (pest->stream, '\n'); + + if (cl && cl->header && *cl->header + && (!pe + || (pe->cluster != cl + && !hol_cluster_is_child (pe->cluster, cl)))) + /* If we're changing clusters, then this must be the start of the + ENTRY's cluster unless that is an ancestor of the previous one + (in which case we had just popped into a sub-cluster for a bit). + If so, then print the cluster's header line. */ + { + int old_wm = __argp_fmtstream_wmargin (pest->stream); + print_header (cl->header, cl->argp, pest); + __argp_fmtstream_set_wmargin (pest->stream, old_wm); + } + + pest->first = 0; + } + else + __argp_fmtstream_puts (pest->stream, ", "); + + indent_to (pest->stream, col); +} + +/* Print help for ENTRY to STREAM. */ +static void +hol_entry_help (struct hol_entry *entry, const struct argp_state *state, + argp_fmtstream_t stream, struct hol_help_state *hhstate) +{ + unsigned num; + const struct argp_option *real = entry->opt, *opt; + char *so = entry->short_options; + int have_long_opt = 0; /* We have any long options. */ + /* Saved margins. */ + int old_lm = __argp_fmtstream_set_lmargin (stream, 0); + int old_wm = __argp_fmtstream_wmargin (stream); + /* PEST is a state block holding some of our variables that we'd like to + share with helper functions. */ + struct pentry_state pest = { entry, stream, hhstate, 1, state }; + + if (! odoc (real)) + for (opt = real, num = entry->num; num > 0; opt++, num--) + if (opt->name && ovisible (opt)) + { + have_long_opt = 1; + break; + } + + /* First emit short options. */ + __argp_fmtstream_set_wmargin (stream, uparams.short_opt_col); /* For truly bizarre cases. */ + for (opt = real, num = entry->num; num > 0; opt++, num--) + if (oshort (opt) && opt->key == *so) + /* OPT has a valid (non shadowed) short option. */ + { + if (ovisible (opt)) + { + comma (uparams.short_opt_col, &pest); + __argp_fmtstream_putc (stream, '-'); + __argp_fmtstream_putc (stream, *so); + if (!have_long_opt || uparams.dup_args) + arg (real, " %s", "[%s]", state->root_argp->argp_domain, stream); + else if (real->arg) + hhstate->suppressed_dup_arg = 1; + } + so++; + } + + /* Now, long options. */ + if (odoc (real)) + /* A `documentation' option. */ + { + __argp_fmtstream_set_wmargin (stream, uparams.doc_opt_col); + for (opt = real, num = entry->num; num > 0; opt++, num--) + if (opt->name && ovisible (opt)) + { + comma (uparams.doc_opt_col, &pest); + /* Calling gettext here isn't quite right, since sorting will + have been done on the original; but documentation options + should be pretty rare anyway... */ + __argp_fmtstream_puts (stream, + dgettext (state->root_argp->argp_domain, + opt->name)); + } + } + else + /* A real long option. */ + { + int first_long_opt = 1; + + __argp_fmtstream_set_wmargin (stream, uparams.long_opt_col); + for (opt = real, num = entry->num; num > 0; opt++, num--) + if (opt->name && ovisible (opt)) + { + comma (uparams.long_opt_col, &pest); + __argp_fmtstream_printf (stream, "--%s", opt->name); + if (first_long_opt || uparams.dup_args) + arg (real, "=%s", "[=%s]", state->root_argp->argp_domain, stream); + else if (real->arg) + hhstate->suppressed_dup_arg = 1; + } + } + + /* Next, documentation strings. */ + __argp_fmtstream_set_lmargin (stream, 0); + + if (pest.first) + { + /* Didn't print any switches, what's up? */ + if (!oshort (real) && !real->name) + /* This is a group header, print it nicely. */ + print_header (real->doc, entry->argp, &pest); + else + /* Just a totally shadowed option or null header; print nothing. */ + goto cleanup; /* Just return, after cleaning up. */ + } + else + { + const char *tstr = real->doc ? dgettext (state->root_argp->argp_domain, + real->doc) : 0; + const char *fstr = filter_doc (tstr, real->key, entry->argp, state); + if (fstr && *fstr) + { + unsigned int col = __argp_fmtstream_point (stream); + + __argp_fmtstream_set_lmargin (stream, uparams.opt_doc_col); + __argp_fmtstream_set_wmargin (stream, uparams.opt_doc_col); + + if (col > (unsigned int) (uparams.opt_doc_col + 3)) + __argp_fmtstream_putc (stream, '\n'); + else if (col >= (unsigned int) uparams.opt_doc_col) + __argp_fmtstream_puts (stream, " "); + else + indent_to (stream, uparams.opt_doc_col); + + __argp_fmtstream_puts (stream, fstr); + } + if (fstr && fstr != tstr) + free ((char *) fstr); + + /* Reset the left margin. */ + __argp_fmtstream_set_lmargin (stream, 0); + __argp_fmtstream_putc (stream, '\n'); + } + + hhstate->prev_entry = entry; + +cleanup: + __argp_fmtstream_set_lmargin (stream, old_lm); + __argp_fmtstream_set_wmargin (stream, old_wm); +} + +/* Output a long help message about the options in HOL to STREAM. */ +static void +hol_help (struct hol *hol, const struct argp_state *state, + argp_fmtstream_t stream) +{ + unsigned num; + struct hol_entry *entry; + struct hol_help_state hhstate = { 0, 0, 0 }; + + for (entry = hol->entries, num = hol->num_entries; num > 0; entry++, num--) + hol_entry_help (entry, state, stream, &hhstate); + + if (hhstate.suppressed_dup_arg && uparams.dup_args_note) + { + const char *tstr = dgettext (state->root_argp->argp_domain, "\ + Mandatory or optional arguments to long options are also mandatory or \ + optional for any corresponding short options."); + const char *fstr = filter_doc (tstr, ARGP_KEY_HELP_DUP_ARGS_NOTE, + state ? state->root_argp : 0, state); + if (fstr && *fstr) + { + __argp_fmtstream_putc (stream, '\n'); + __argp_fmtstream_puts (stream, fstr); + __argp_fmtstream_putc (stream, '\n'); + } + if (fstr && fstr != tstr) + free ((char *) fstr); + } +} + +/* Helper functions for hol_usage. */ + +/* If OPT is a short option without an arg, append its key to the string + pointer pointer to by COOKIE, and advance the pointer. */ +static int +add_argless_short_opt (const struct argp_option *opt, + const struct argp_option *real, + const char *domain, void *cookie) +{ + char **snao_end = cookie; + if (!(opt->arg || real->arg) + && !((opt->flags | real->flags) & OPTION_NO_USAGE)) + *(*snao_end)++ = opt->key; + return 0; +} + +/* If OPT is a short option with an arg, output a usage entry for it to the + stream pointed at by COOKIE. */ +static int +usage_argful_short_opt (const struct argp_option *opt, + const struct argp_option *real, + const char *domain, void *cookie) +{ + argp_fmtstream_t stream = cookie; + const char *arg = opt->arg; + int flags = opt->flags | real->flags; + + if (! arg) + arg = real->arg; + + if (arg && !(flags & OPTION_NO_USAGE)) + { + arg = dgettext (domain, arg); + + if (flags & OPTION_ARG_OPTIONAL) + __argp_fmtstream_printf (stream, " [-%c[%s]]", opt->key, arg); + else + { + /* Manually do line wrapping so that it (probably) won't + get wrapped at the embedded space. */ + space (stream, 6 + strlen (arg)); + __argp_fmtstream_printf (stream, "[-%c %s]", opt->key, arg); + } + } + + return 0; +} + +/* Output a usage entry for the long option opt to the stream pointed at by + COOKIE. */ +static int +usage_long_opt (const struct argp_option *opt, + const struct argp_option *real, + const char *domain, void *cookie) +{ + argp_fmtstream_t stream = cookie; + const char *arg = opt->arg; + int flags = opt->flags | real->flags; + + if (! arg) + arg = real->arg; + + if (! (flags & OPTION_NO_USAGE)) + { + if (arg) + { + arg = dgettext (domain, arg); + if (flags & OPTION_ARG_OPTIONAL) + __argp_fmtstream_printf (stream, " [--%s[=%s]]", opt->name, arg); + else + __argp_fmtstream_printf (stream, " [--%s=%s]", opt->name, arg); + } + else + __argp_fmtstream_printf (stream, " [--%s]", opt->name); + } + + return 0; +} + +/* Print a short usage description for the arguments in HOL to STREAM. */ +static void +hol_usage (struct hol *hol, argp_fmtstream_t stream) +{ + if (hol->num_entries > 0) + { + unsigned nentries; + struct hol_entry *entry; + char *short_no_arg_opts = alloca (strlen (hol->short_options) + 1); + char *snao_end = short_no_arg_opts; + + /* First we put a list of short options without arguments. */ + for (entry = hol->entries, nentries = hol->num_entries + ; nentries > 0 + ; entry++, nentries--) + hol_entry_short_iterate (entry, add_argless_short_opt, + entry->argp->argp_domain, &snao_end); + if (snao_end > short_no_arg_opts) + { + *snao_end++ = 0; + __argp_fmtstream_printf (stream, " [-%s]", short_no_arg_opts); + } + + /* Now a list of short options *with* arguments. */ + for (entry = hol->entries, nentries = hol->num_entries + ; nentries > 0 + ; entry++, nentries--) + hol_entry_short_iterate (entry, usage_argful_short_opt, + entry->argp->argp_domain, stream); + + /* Finally, a list of long options (whew!). */ + for (entry = hol->entries, nentries = hol->num_entries + ; nentries > 0 + ; entry++, nentries--) + hol_entry_long_iterate (entry, usage_long_opt, + entry->argp->argp_domain, stream); + } +} + +/* Make a HOL containing all levels of options in ARGP. CLUSTER is the + cluster in which ARGP's entries should be clustered, or 0. */ +static struct hol * +argp_hol (const struct argp *argp, struct hol_cluster *cluster) +{ + const struct argp_child *child = argp->children; + struct hol *hol = make_hol (argp, cluster); + if (child) + while (child->argp) + { + struct hol_cluster *child_cluster = + ((child->group || child->header) + /* Put CHILD->argp within its own cluster. */ + ? hol_add_cluster (hol, child->group, child->header, + child - argp->children, cluster, argp) + /* Just merge it into the parent's cluster. */ + : cluster); + hol_append (hol, argp_hol (child->argp, child_cluster)) ; + child++; + } + return hol; +} + +/* Calculate how many different levels with alternative args strings exist in + ARGP. */ +static size_t +argp_args_levels (const struct argp *argp) +{ + size_t levels = 0; + const struct argp_child *child = argp->children; + + if (argp->args_doc && strchr (argp->args_doc, '\n')) + levels++; + + if (child) + while (child->argp) + levels += argp_args_levels ((child++)->argp); + + return levels; +} + +/* Print all the non-option args documented in ARGP to STREAM. Any output is + preceded by a space. LEVELS is a pointer to a byte vector the length + returned by argp_args_levels; it should be initialized to zero, and + updated by this routine for the next call if ADVANCE is true. True is + returned as long as there are more patterns to output. */ +static int +argp_args_usage (const struct argp *argp, const struct argp_state *state, + char **levels, int advance, argp_fmtstream_t stream) +{ + char *our_level = *levels; + int multiple = 0; + const struct argp_child *child = argp->children; + const char *tdoc = dgettext (argp->argp_domain, argp->args_doc), *nl = 0; + const char *fdoc = filter_doc (tdoc, ARGP_KEY_HELP_ARGS_DOC, argp, state); + + if (fdoc) + { + const char *cp = fdoc; + nl = strchr (cp, '\n'); + if (!nl) + { + nl = cp; + while (*nl != '\0') nl++; + } + + if (*nl != '\0') + /* This is a `multi-level' args doc; advance to the correct position + as determined by our state in LEVELS, and update LEVELS. */ + { + int i; + multiple = 1; + for (i = 0; i < *our_level; i++) + { + cp = nl + 1; + nl = strchr (cp, '\n'); + if (!nl) + { + nl = cp; + while (*nl != '\0') nl++; + } + } + (*levels)++; + } + + /* Manually do line wrapping so that it (probably) won't get wrapped at + any embedded spaces. */ + space (stream, 1 + nl - cp); + + __argp_fmtstream_write (stream, cp, nl - cp); + } + if (fdoc && fdoc != tdoc) + free ((char *)fdoc); /* Free user's modified doc string. */ + + if (child) + while (child->argp) + advance = !argp_args_usage ((child++)->argp, state, levels, advance, stream); + + if (advance && multiple) + { + /* Need to increment our level. */ + if (*nl) + /* There's more we can do here. */ + { + (*our_level)++; + advance = 0; /* Our parent shouldn't advance also. */ + } + else if (*our_level > 0) + /* We had multiple levels, but used them up; reset to zero. */ + *our_level = 0; + } + + return !advance; +} + +/* Print the documentation for ARGP to STREAM; if POST is false, then + everything preceeding a `\v' character in the documentation strings (or + the whole string, for those with none) is printed, otherwise, everything + following the `\v' character (nothing for strings without). Each separate + bit of documentation is separated a blank line, and if PRE_BLANK is true, + then the first is as well. If FIRST_ONLY is true, only the first + occurrence is output. Returns true if anything was output. */ +static int +argp_doc (const struct argp *argp, const struct argp_state *state, + int post, int pre_blank, int first_only, + argp_fmtstream_t stream) +{ + const char *text; + const char *inp_text; + char *tmp_text; + void *input = 0; + int anything = 0; + size_t inp_text_limit = 0; + const char *doc = dgettext (argp->argp_domain, argp->doc); + const struct argp_child *child = argp->children; + + if (doc) + { + char *vt = strchr (doc, '\v'); + inp_text = post ? (vt ? vt + 1 : 0) : doc; + inp_text_limit = (!post && vt) ? (vt - doc) : 0; + } + else + inp_text = 0; + + if (argp->help_filter) + /* We have to filter the doc strings. */ + { + if (inp_text_limit) + { + /* Copy INP_TEXT so that it's nul-terminated. */ + tmp_text = _malloc_r (_REENT, inp_text_limit); + strncpy (tmp_text, inp_text, inp_text_limit); + _free_r (_REENT, inp_text); + inp_text = tmp_text; + } + input = __argp_input (argp, state); + text = + (*argp->help_filter) (post + ? ARGP_KEY_HELP_POST_DOC + : ARGP_KEY_HELP_PRE_DOC, + inp_text, input); + } + else + text = (const char *) inp_text; + + if (text) + { + if (pre_blank) + __argp_fmtstream_putc (stream, '\n'); + + if (text == inp_text && inp_text_limit) + __argp_fmtstream_write (stream, inp_text, inp_text_limit); + else + __argp_fmtstream_puts (stream, text); + + if (__argp_fmtstream_point (stream) > __argp_fmtstream_lmargin (stream)) + __argp_fmtstream_putc (stream, '\n'); + + anything = 1; + } + + if (text && text != inp_text) + free ((char *) text); /* Free TEXT returned from the help filter. */ + if (inp_text && inp_text_limit && argp->help_filter) + free ((char *) inp_text); /* We copied INP_TEXT, so free it now. */ + + if (post && argp->help_filter) + /* Now see if we have to output a ARGP_KEY_HELP_EXTRA text. */ + { + text = (*argp->help_filter) (ARGP_KEY_HELP_EXTRA, 0, input); + if (text) + { + if (anything || pre_blank) + __argp_fmtstream_putc (stream, '\n'); + __argp_fmtstream_puts (stream, text); + free ((char *) text); + if (__argp_fmtstream_point (stream) + > __argp_fmtstream_lmargin (stream)) + __argp_fmtstream_putc (stream, '\n'); + anything = 1; + } + } + + if (child) + while (child->argp && !(first_only && anything)) + anything |= + argp_doc ((child++)->argp, state, + post, anything || pre_blank, first_only, + stream); + + return anything; +} + +/* Output a usage message for ARGP to STREAM. If called from + argp_state_help, STATE is the relevent parsing state. FLAGS are from the + set ARGP_HELP_*. NAME is what to use wherever a `program name' is + needed. */ +static void +_help (const struct argp *argp, const struct argp_state *state, FILE *stream, + unsigned flags, char *name) +{ + int anything = 0; /* Whether we've output anything. */ + struct hol *hol = 0; + argp_fmtstream_t fs; + + if (! stream) + return; + + _flockfile (stream); + + if (! uparams.valid) + fill_in_uparams (state); + + fs = __argp_make_fmtstream (stream, 0, uparams.rmargin, 0); + if (! fs) + { + _funlockfile (stream); + return; + } + + if (flags & (ARGP_HELP_USAGE | ARGP_HELP_SHORT_USAGE | ARGP_HELP_LONG)) + { + hol = argp_hol (argp, 0); + + /* If present, these options always come last. */ + hol_set_group (hol, "help", -1); + hol_set_group (hol, "version", -1); + + hol_sort (hol); + } + + if (flags & (ARGP_HELP_USAGE | ARGP_HELP_SHORT_USAGE)) + /* Print a short `Usage:' message. */ + { + int first_pattern = 1, more_patterns; + size_t num_pattern_levels = argp_args_levels (argp); + char *pattern_levels = alloca (num_pattern_levels); + + memset (pattern_levels, 0, num_pattern_levels); + + do + { + int old_lm; + int old_wm = __argp_fmtstream_set_wmargin (fs, uparams.usage_indent); + char *levels = pattern_levels; + + if (first_pattern) + __argp_fmtstream_printf (fs, "%s %s", + dgettext (argp->argp_domain, "Usage:"), + name); + else + __argp_fmtstream_printf (fs, "%s %s", + dgettext (argp->argp_domain, " or: "), + name); + + /* We set the lmargin as well as the wmargin, because hol_usage + manually wraps options with newline to avoid annoying breaks. */ + old_lm = __argp_fmtstream_set_lmargin (fs, uparams.usage_indent); + + if (flags & ARGP_HELP_SHORT_USAGE) + /* Just show where the options go. */ + { + if (hol->num_entries > 0) + __argp_fmtstream_puts (fs, dgettext (argp->argp_domain, + " [OPTION...]")); + } + else + /* Actually print the options. */ + { + hol_usage (hol, fs); + flags |= ARGP_HELP_SHORT_USAGE; /* But only do so once. */ + } + + more_patterns = argp_args_usage (argp, state, &levels, 1, fs); + + __argp_fmtstream_set_wmargin (fs, old_wm); + __argp_fmtstream_set_lmargin (fs, old_lm); + + __argp_fmtstream_putc (fs, '\n'); + anything = 1; + + first_pattern = 0; + } + while (more_patterns); + } + + if (flags & ARGP_HELP_PRE_DOC) + anything |= argp_doc (argp, state, 0, 0, 1, fs); + + if (flags & ARGP_HELP_SEE) + { + __argp_fmtstream_printf (fs, dgettext (argp->argp_domain, "\ + Try `%s --help' or `%s --usage' for more information.\n"), + name, name); + anything = 1; + } + + if (flags & ARGP_HELP_LONG) + /* Print a long, detailed help message. */ + { + /* Print info about all the options. */ + if (hol->num_entries > 0) + { + if (anything) + __argp_fmtstream_putc (fs, '\n'); + hol_help (hol, state, fs); + anything = 1; + } + } + + if (flags & ARGP_HELP_POST_DOC) + /* Print any documentation strings at the end. */ + anything |= argp_doc (argp, state, 1, anything, 0, fs); + + if ((flags & ARGP_HELP_BUG_ADDR) && argp_program_bug_address) + { + if (anything) + __argp_fmtstream_putc (fs, '\n'); + __argp_fmtstream_printf (fs, dgettext (argp->argp_domain, + "Report bugs to %s.\n"), + argp_program_bug_address); + anything = 1; + } + + _funlockfile (stream); + + if (hol) + hol_free (hol); + + __argp_fmtstream_free (fs); +} + +/* Output a usage message for ARGP to STREAM. FLAGS are from the set + ARGP_HELP_*. NAME is what to use wherever a `program name' is needed. */ +void __argp_help (const struct argp *argp, FILE *stream, + unsigned flags, char *name) +{ + _help (argp, 0, stream, flags, name); +} +#ifdef weak_alias +weak_alias (__argp_help, argp_help) +#endif + +/* Output, if appropriate, a usage message for STATE to STREAM. FLAGS are + from the set ARGP_HELP_*. */ +void +__argp_state_help (const struct argp_state *state, FILE *stream, unsigned flags) +{ + if ((!state || ! (state->flags & ARGP_NO_ERRS)) && stream) + { + if (state && (state->flags & ARGP_LONG_ONLY)) + flags |= ARGP_HELP_LONG_ONLY; + + _help (state ? state->root_argp : 0, state, stream, flags, + state ? state->name : program_invocation_short_name); + + if (!state || ! (state->flags & ARGP_NO_EXIT)) + { + if (flags & ARGP_HELP_EXIT_ERR) + exit (argp_err_exit_status); + if (flags & ARGP_HELP_EXIT_OK) + exit (0); + } + } +} +#ifdef weak_alias +weak_alias (__argp_state_help, argp_state_help) +#endif + +/* If appropriate, print the printf string FMT and following args, preceded + by the program name and `:', to stderr, and followed by a `Try ... --help' + message, then exit (1). */ +void +__argp_error (const struct argp_state *state, const char *fmt, ...) +{ + if (!state || !(state->flags & ARGP_NO_ERRS)) + { + FILE *stream = state ? state->err_stream : stderr; + + if (stream) + { + va_list ap; + + _flockfile (stream); + + va_start (ap, fmt); + +#ifdef USE_IN_LIBIO + if (_IO_fwide (stream, 0) > 0) + { + char *buf; + + __asprintf (&buf, fmt, ap); + + __fwprintf (stream, L"%s: %s\n", + state ? state->name : program_invocation_short_name, + buf); + + free (buf); + } + else +#endif + { + fputs (state + ? state->name : program_invocation_short_name, + stream); + putc_unlocked (':', stream); + putc_unlocked (' ', stream); + + vfprintf (stream, fmt, ap); + + putc_unlocked ('\n', stream); + } + + __argp_state_help (state, stream, ARGP_HELP_STD_ERR); + + va_end (ap); + + _funlockfile (stream); + } + } +} +#ifdef weak_alias +weak_alias (__argp_error, argp_error) +#endif + +/* Similar to the standard gnu error-reporting function error(), but will + respect the ARGP_NO_EXIT and ARGP_NO_ERRS flags in STATE, and will print + to STATE->err_stream. This is useful for argument parsing code that is + shared between program startup (when exiting is desired) and runtime + option parsing (when typically an error code is returned instead). The + difference between this function and argp_error is that the latter is for + *parsing errors*, and the former is for other problems that occur during + parsing but don't reflect a (syntactic) problem with the input. */ +void +__argp_failure (const struct argp_state *state, int status, int errnum, + const char *fmt, ...) +{ + if (!state || !(state->flags & ARGP_NO_ERRS)) + { + FILE *stream = state ? state->err_stream : stderr; + + if (stream) + { + _flockfile (stream); + +#ifdef USE_IN_LIBIO + if (_IO_fwide (stream, 0) > 0) + __fwprintf (stream, L"%s", + state ? state->name : program_invocation_short_name); + else +#endif + fputs (state + ? state->name : program_invocation_short_name, + stream); + + if (fmt) + { + va_list ap; + + va_start (ap, fmt); +#ifdef USE_IN_LIBIO + if (_IO_fwide (stream, 0) > 0) + { + char *buf; + + __asprintf (&buf, fmt, ap); + + __fwprintf (stream, L": %s", buf); + + free (buf); + } + else +#endif + { + putc_unlocked (':', stream); + putc_unlocked (' ', stream); + + vfprintf (stream, fmt, ap); + } + + va_end (ap); + } + + if (errnum) + { + char buf[200]; + +#ifdef USE_IN_LIBIO + if (_IO_fwide (stream, 0) > 0) + __fwprintf (stream, L": %s", + strerror_r (errnum, buf, sizeof (buf))); + else +#endif + { + putc_unlocked (':', stream); + putc_unlocked (' ', stream); + fputs (strerror_r (errnum, buf, sizeof (buf)), stream); + } + } + +#ifdef USE_IN_LIBIO + if (_IO_fwide (stream, 0) > 0) + putwc_unlocked (L'\n', stream); + else +#endif + putc_unlocked ('\n', stream); + + _funlockfile (stream); + + if (status && (!state || !(state->flags & ARGP_NO_EXIT))) + exit (status); + } + } +} +#ifdef weak_alias +weak_alias (__argp_failure, argp_failure) +#endif diff --git a/libraries/platforms/dromajo-vcs/software/src/argp/argp-parse.c b/libraries/platforms/dromajo-vcs/software/src/argp/argp-parse.c new file mode 100644 index 000000000..afa6a3745 --- /dev/null +++ b/libraries/platforms/dromajo-vcs/software/src/argp/argp-parse.c @@ -0,0 +1,1026 @@ +// Copyright (c) 2020, University of Washington All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// Redistributions of source code must retain the above copyright notice, this list +// of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above copyright notice, this +// list of conditions and the following disclaimer in the documentation and/or +// other materials provided with the distribution. +// +// Neither the name of the copyright holder nor the names of its contributors may +// be used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// This is a slightly modified copy of the argp library from Newlib + +/* Hierarchial argument parsing, layered over getopt + Copyright (C) 1995, 96, 97, 98, 99, 2000 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Written by Miles Bader . + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#ifndef _ +/* This is for other GNU distributions with internationalized messages. + When compiling libc, the _ macro is predefined. */ +# if defined HAVE_LIBINTL_H || defined _LIBC +# include +# ifdef _LIBC +# undef dgettext +# define dgettext(domain, msgid) __dcgettext (domain, msgid, LC_MESSAGES) +# endif +# else +# define dgettext(domain, msgid) (msgid) +# define gettext(msgid) (msgid) +# endif +#endif +#ifndef N_ +# define N_(msgid) (msgid) +#endif + +#if _LIBC - 0 +#include +#else +#ifdef HAVE_CTHREADS_H +#include +#endif +#endif /* _LIBC */ + +#include +#include + +/* Getopt return values. */ +#define KEY_END (-1) /* The end of the options. */ +#define KEY_ARG 1 /* A non-option argument. */ +#define KEY_ERR '?' /* An error parsing the options. */ + +/* The meta-argument used to prevent any further arguments being interpreted + as options. */ +#define QUOTE "--" + +/* The number of bits we steal in a long-option value for our own use. */ +#define GROUP_BITS CHAR_BIT + +/* The number of bits available for the user value. */ +#define USER_BITS ((sizeof ((struct option *)0)->val * CHAR_BIT) - GROUP_BITS) +#define USER_MASK ((1 << USER_BITS) - 1) + +/* EZ alias for ARGP_ERR_UNKNOWN. */ +#define EBADKEY ARGP_ERR_UNKNOWN + +/* Default options. */ + +/* When argp is given the --HANG switch, _ARGP_HANG is set and argp will sleep + for one second intervals, decrementing _ARGP_HANG until it's zero. Thus + you can force the program to continue by attaching a debugger and setting + it to 0 yourself. */ +volatile int _argp_hang; + +#define OPT_PROGNAME -2 +#define OPT_USAGE -3 +#define OPT_HANG -4 + +static const struct argp_option argp_default_options[] = +{ + {"help", '?', 0, 0, N_("Give this help list"), -1}, + {"usage", OPT_USAGE, 0, 0, N_("Give a short usage message")}, + {"program-name",OPT_PROGNAME,"NAME", OPTION_HIDDEN, N_("Set the program name")}, + {"HANG", OPT_HANG, "SECS", OPTION_ARG_OPTIONAL | OPTION_HIDDEN, + N_("Hang for SECS seconds (default 3600)")}, + {0, 0} +}; + +static error_t +argp_default_parser (int key, char *arg, struct argp_state *state) +{ + switch (key) + { + case '?': + __argp_state_help (state, state->out_stream, ARGP_HELP_STD_HELP); + break; + case OPT_USAGE: + __argp_state_help (state, state->out_stream, + ARGP_HELP_USAGE | ARGP_HELP_EXIT_OK); + break; + + case OPT_PROGNAME: /* Set the program name. */ + program_invocation_name = arg; + + /* [Note that some systems only have PROGRAM_INVOCATION_SHORT_NAME (aka + __PROGNAME), in which case, PROGRAM_INVOCATION_NAME is just defined + to be that, so we have to be a bit careful here.] */ + arg = strrchr (arg, '/'); + if (arg) + program_invocation_short_name = arg + 1; + else + program_invocation_short_name = program_invocation_name; + + /* Update what we use for messages. */ + state->name = program_invocation_short_name; + + if ((state->flags & (ARGP_PARSE_ARGV0 | ARGP_NO_ERRS)) + == ARGP_PARSE_ARGV0) + /* Update what getopt uses too. */ + state->argv[0] = program_invocation_name; + + break; + + case OPT_HANG: + _argp_hang = atoi (arg ? arg : "3600"); + while (_argp_hang-- > 0) + // Sripathi: + // Use a for loop to hang the execution rather than sleep + // since it is not implemented + // This will consume power but we don't really care since we will + // run a full-featured linux in real use cases and this will not be a problem + // Also, this a single-thread execution and there is nothing to context switch to + // while sleeping, so it shouldn't matter anyways. + // Choosing an arbitrary number to sleep. This necessarily may not map to 1 second + for (int i = 0; i < 1000; i++) + for (int j = 0; j <= (1 << 31); j++) + // Use this to prevent the compiler from optimizing this piece of code out. + __asm__ __volatile__("" : "+g" (i), "+g" (j) : :); + break; + + default: + return EBADKEY; + } + return 0; +} + +static const struct argp argp_default_argp = + {argp_default_options, &argp_default_parser, NULL, NULL, NULL, NULL, "libc"}; + + +static const struct argp_option argp_version_options[] = +{ + {"version", 'V', 0, 0, N_("Print program version"), -1}, + {0, 0} +}; + +static error_t +argp_version_parser (int key, char *arg, struct argp_state *state) +{ + switch (key) + { + case 'V': + if (argp_program_version_hook) + (*argp_program_version_hook) (state->out_stream, state); + else if (argp_program_version) + fprintf (state->out_stream, "%s\n", argp_program_version); + else + __argp_error (state, dgettext (state->root_argp->argp_domain, + "(PROGRAM ERROR) No version known!?")); + if (! (state->flags & ARGP_NO_EXIT)) + exit (0); + break; + default: + return EBADKEY; + } + return 0; +} + +static const struct argp argp_version_argp = + {argp_version_options, &argp_version_parser, NULL, NULL, NULL, NULL, "libc"}; + +/* Returns the offset into the getopt long options array LONG_OPTIONS of a + long option with called NAME, or -1 if none is found. Passing NULL as + NAME will return the number of options. */ +static int +find_long_option (struct option *long_options, const char *name) +{ + struct option *l = long_options; + while (l->name != NULL) + if (name != NULL && strcmp (l->name, name) == 0) + return l - long_options; + else + l++; + if (name == NULL) + return l - long_options; + else + return -1; +} + +/* If we can, we regulate access to getopt, which is non-reentrant, with a + mutex. Since the case we're trying to guard against is two different + threads interfering, and it's possible that someone might want to call + argp_parse recursively (they're careful), we use a recursive lock if + possible. */ + +#if _LIBC - 0 + +__libc_lock_define_initialized_recursive (static, getopt_lock) +#define LOCK_GETOPT __libc_lock_lock_recursive (getopt_lock) +#define UNLOCK_GETOPT __libc_lock_unlock_recursive (getopt_lock) + +#else /* !_LIBC */ +#ifdef HAVE_CTHREADS_H + +static struct mutex getopt_lock = MUTEX_INITIALIZER; +#define LOCK_GETOPT mutex_lock (&getopt_lock) +#define UNLOCK_GETOPT mutex_unlock (&getopt_lock) + +#else /* !HAVE_CTHREADS_H */ + +#define LOCK_GETOPT (void)0 +#define UNLOCK_GETOPT (void)0 + +#endif /* HAVE_CTHREADS_H */ +#endif /* _LIBC */ + +/* This hack to allow programs that know what's going on to call argp + recursively. If someday argp is changed not to use the non-reentrant + getopt interface, we can get rid of this shit. XXX */ +void +_argp_unlock_xxx (void) +{ + UNLOCK_GETOPT; +} + +/* The state of a `group' during parsing. Each group corresponds to a + particular argp structure from the tree of such descending from the top + level argp passed to argp_parse. */ +struct group +{ + /* This group's parsing function. */ + argp_parser_t parser; + + /* Which argp this group is from. */ + const struct argp *argp; + + /* Points to the point in SHORT_OPTS corresponding to the end of the short + options for this group. We use it to determine from which group a + particular short options is from. */ + char *short_end; + + /* The number of non-option args sucessfully handled by this parser. */ + unsigned args_processed; + + /* This group's parser's parent's group. */ + struct group *parent; + unsigned parent_index; /* And the our position in the parent. */ + + /* These fields are swapped into and out of the state structure when + calling this group's parser. */ + void *input, **child_inputs; + void *hook; +}; + +/* Call GROUP's parser with KEY and ARG, swapping any group-specific info + from STATE before calling, and back into state afterwards. If GROUP has + no parser, EBADKEY is returned. */ +static error_t +group_parse (struct group *group, struct argp_state *state, int key, char *arg) +{ + if (group->parser) + { + error_t err; + state->hook = group->hook; + state->input = group->input; + state->child_inputs = group->child_inputs; + state->arg_num = group->args_processed; + err = (*group->parser)(key, arg, state); + group->hook = state->hook; + return err; + } + else + return EBADKEY; +} + +struct parser +{ + const struct argp *argp; + + /* SHORT_OPTS is the getopt short options string for the union of all the + groups of options. */ + char *short_opts; + /* LONG_OPTS is the array of getop long option structures for the union of + all the groups of options. */ + struct option *long_opts; + + /* States of the various parsing groups. */ + struct group *groups; + /* The end of the GROUPS array. */ + struct group *egroup; + /* An vector containing storage for the CHILD_INPUTS field in all groups. */ + void **child_inputs; + + /* True if we think using getopt is still useful; if false, then + remaining arguments are just passed verbatim with ARGP_KEY_ARG. This is + cleared whenever getopt returns KEY_END, but may be set again if the user + moves the next argument pointer backwards. */ + int try_getopt; + + /* State block supplied to parsing routines. */ + struct argp_state state; + + /* Memory used by this parser. */ + void *storage; +}; + +/* The next usable entries in the various parser tables being filled in by + convert_options. */ +struct parser_convert_state +{ + struct parser *parser; + char *short_end; + struct option *long_end; + void **child_inputs_end; +}; + +/* Converts all options in ARGP (which is put in GROUP) and ancestors + into getopt options stored in SHORT_OPTS and LONG_OPTS; SHORT_END and + CVT->LONG_END are the points at which new options are added. Returns the + next unused group entry. CVT holds state used during the conversion. */ +static struct group * +convert_options (const struct argp *argp, + struct group *parent, unsigned parent_index, + struct group *group, struct parser_convert_state *cvt) +{ + /* REAL is the most recent non-alias value of OPT. */ + const struct argp_option *real = argp->options; + const struct argp_child *children = argp->children; + + if (real || argp->parser) + { + const struct argp_option *opt; + + if (real) + for (opt = real; !__option_is_end (opt); opt++) + { + if (! (opt->flags & OPTION_ALIAS)) + /* OPT isn't an alias, so we can use values from it. */ + real = opt; + + if (! (real->flags & OPTION_DOC)) + /* A real option (not just documentation). */ + { + if (__option_is_short (opt)) + /* OPT can be used as a short option. */ + { + *cvt->short_end++ = opt->key; + if (real->arg) + { + *cvt->short_end++ = ':'; + if (real->flags & OPTION_ARG_OPTIONAL) + *cvt->short_end++ = ':'; + } + *cvt->short_end = '\0'; /* keep 0 terminated */ + } + + if (opt->name + && find_long_option (cvt->parser->long_opts, opt->name) < 0) + /* OPT can be used as a long option. */ + { + cvt->long_end->name = opt->name; + cvt->long_end->has_arg = + (real->arg + ? (real->flags & OPTION_ARG_OPTIONAL + ? optional_argument + : required_argument) + : no_argument); + cvt->long_end->flag = 0; + /* we add a disambiguating code to all the user's + values (which is removed before we actually call + the function to parse the value); this means that + the user loses use of the high 8 bits in all his + values (the sign of the lower bits is preserved + however)... */ + cvt->long_end->val = + ((opt->key | real->key) & USER_MASK) + + (((group - cvt->parser->groups) + 1) << USER_BITS); + + /* Keep the LONG_OPTS list terminated. */ + (++cvt->long_end)->name = NULL; + } + } + } + + group->parser = argp->parser; + group->argp = argp; + group->short_end = cvt->short_end; + group->args_processed = 0; + group->parent = parent; + group->parent_index = parent_index; + group->input = 0; + group->hook = 0; + group->child_inputs = 0; + + if (children) + /* Assign GROUP's CHILD_INPUTS field some space from + CVT->child_inputs_end.*/ + { + unsigned num_children = 0; + while (children[num_children].argp) + num_children++; + group->child_inputs = cvt->child_inputs_end; + cvt->child_inputs_end += num_children; + } + + parent = group++; + } + else + parent = 0; + + if (children) + { + unsigned index = 0; + while (children->argp) + group = + convert_options (children++->argp, parent, index++, group, cvt); + } + + return group; +} + +/* Find the merged set of getopt options, with keys appropiately prefixed. */ +static void +parser_convert (struct parser *parser, const struct argp *argp, int flags) +{ + struct parser_convert_state cvt; + + cvt.parser = parser; + cvt.short_end = parser->short_opts; + cvt.long_end = parser->long_opts; + cvt.child_inputs_end = parser->child_inputs; + + if (flags & ARGP_IN_ORDER) + *cvt.short_end++ = '-'; + else if (flags & ARGP_NO_ARGS) + *cvt.short_end++ = '+'; + *cvt.short_end = '\0'; + + cvt.long_end->name = NULL; + + parser->argp = argp; + + if (argp) + parser->egroup = convert_options (argp, 0, 0, parser->groups, &cvt); + else + parser->egroup = parser->groups; /* No parsers at all! */ +} + +/* Lengths of various parser fields which we will allocated. */ +struct parser_sizes +{ + size_t short_len; /* Getopt short options string. */ + size_t long_len; /* Getopt long options vector. */ + size_t num_groups; /* Group structures we allocate. */ + size_t num_child_inputs; /* Child input slots. */ +}; + +/* For ARGP, increments the NUM_GROUPS field in SZS by the total number of + argp structures descended from it, and the SHORT_LEN & LONG_LEN fields by + the maximum lengths of the resulting merged getopt short options string and + long-options array, respectively. */ +static void +calc_sizes (const struct argp *argp, struct parser_sizes *szs) +{ + const struct argp_child *child = argp->children; + const struct argp_option *opt = argp->options; + + if (opt || argp->parser) + { + szs->num_groups++; + if (opt) + { + int num_opts = 0; + while (!__option_is_end (opt++)) + num_opts++; + szs->short_len += num_opts * 3; /* opt + up to 2 `:'s */ + szs->long_len += num_opts; + } + } + + if (child) + while (child->argp) + { + calc_sizes ((child++)->argp, szs); + szs->num_child_inputs++; + } +} + +/* Initializes PARSER to parse ARGP in a manner described by FLAGS. */ +static error_t +parser_init (struct parser *parser, const struct argp *argp, + int argc, char **argv, int flags, void *input) +{ + error_t err = 0; + struct group *group; + struct parser_sizes szs; + + szs.short_len = (flags & ARGP_NO_ARGS) ? 0 : 1; + szs.long_len = 0; + szs.num_groups = 0; + szs.num_child_inputs = 0; + + if (argp) + calc_sizes (argp, &szs); + + /* Lengths of the various bits of storage used by PARSER. */ +#define GLEN (szs.num_groups + 1) * sizeof (struct group) +#define CLEN (szs.num_child_inputs * sizeof (void *)) +#define LLEN ((szs.long_len + 1) * sizeof (struct option)) +#define SLEN (szs.short_len + 1) + + parser->storage = malloc (GLEN + CLEN + LLEN + SLEN); + if (! parser->storage) + return ENOMEM; + + parser->groups = parser->storage; + parser->child_inputs = parser->storage + GLEN; + parser->long_opts = parser->storage + GLEN + CLEN; + parser->short_opts = parser->storage + GLEN + CLEN + LLEN; + + memset (parser->child_inputs, 0, szs.num_child_inputs * sizeof (void *)); + parser_convert (parser, argp, flags); + + memset (&parser->state, 0, sizeof (struct argp_state)); + parser->state.root_argp = parser->argp; + parser->state.argc = argc; + parser->state.argv = argv; + parser->state.flags = flags; + parser->state.err_stream = stderr; + parser->state.out_stream = stdout; + parser->state.next = 0; /* Tell getopt to initialize. */ + parser->state.pstate = parser; + + parser->try_getopt = 1; + + /* Call each parser for the first time, giving it a chance to propagate + values to child parsers. */ + if (parser->groups < parser->egroup) + parser->groups->input = input; + for (group = parser->groups; + group < parser->egroup && (!err || err == EBADKEY); + group++) + { + if (group->parent) + /* If a child parser, get the initial input value from the parent. */ + group->input = group->parent->child_inputs[group->parent_index]; + + if (!group->parser + && group->argp->children && group->argp->children->argp) + /* For the special case where no parsing function is supplied for an + argp, propagate its input to its first child, if any (this just + makes very simple wrapper argps more convenient). */ + group->child_inputs[0] = group->input; + + err = group_parse (group, &parser->state, ARGP_KEY_INIT, 0); + } + if (err == EBADKEY) + err = 0; /* Some parser didn't understand. */ + + if (err) + return err; + + /* Getopt is (currently) non-reentrant. */ + LOCK_GETOPT; + + if (parser->state.flags & ARGP_NO_ERRS) + { + opterr = 0; + if (parser->state.flags & ARGP_PARSE_ARGV0) + /* getopt always skips ARGV[0], so we have to fake it out. As long + as OPTERR is 0, then it shouldn't actually try to access it. */ + parser->state.argv--, parser->state.argc++; + } + else + opterr = 1; /* Print error messages. */ + + if (parser->state.argv == argv && argv[0]) + /* There's an argv[0]; use it for messages. */ + { + char *short_name = strrchr (argv[0], '/'); + parser->state.name = short_name ? short_name + 1 : argv[0]; + } + else + parser->state.name = program_invocation_short_name; + + return 0; +} + +/* Free any storage consumed by PARSER (but not PARSER itself). */ +static error_t +parser_finalize (struct parser *parser, + error_t err, int arg_ebadkey, int *end_index) +{ + struct group *group; + + UNLOCK_GETOPT; + + if (err == EBADKEY && arg_ebadkey) + /* Suppress errors generated by unparsed arguments. */ + err = 0; + + if (! err) + { + if (parser->state.next == parser->state.argc) + /* We successfully parsed all arguments! Call all the parsers again, + just a few more times... */ + { + for (group = parser->groups; + group < parser->egroup && (!err || err==EBADKEY); + group++) + if (group->args_processed == 0) + err = group_parse (group, &parser->state, ARGP_KEY_NO_ARGS, 0); + for (group = parser->egroup - 1; + group >= parser->groups && (!err || err==EBADKEY); + group--) + err = group_parse (group, &parser->state, ARGP_KEY_END, 0); + + if (err == EBADKEY) + err = 0; /* Some parser didn't understand. */ + + /* Tell the user that all arguments are parsed. */ + if (end_index) + *end_index = parser->state.next; + } + else if (end_index) + /* Return any remaining arguments to the user. */ + *end_index = parser->state.next; + else + /* No way to return the remaining arguments, they must be bogus. */ + { + if (!(parser->state.flags & ARGP_NO_ERRS) + && parser->state.err_stream) + fprintf (parser->state.err_stream, + dgettext (parser->argp->argp_domain, + "%s: Too many arguments\n"), + parser->state.name); + err = EBADKEY; + } + } + + /* Okay, we're all done, with either an error or success; call the parsers + to indicate which one. */ + + if (err) + { + /* Maybe print an error message. */ + if (err == EBADKEY) + /* An appropriate message describing what the error was should have + been printed earlier. */ + __argp_state_help (&parser->state, parser->state.err_stream, + ARGP_HELP_STD_ERR); + + /* Since we didn't exit, give each parser an error indication. */ + for (group = parser->groups; group < parser->egroup; group++) + group_parse (group, &parser->state, ARGP_KEY_ERROR, 0); + } + else + /* Notify parsers of success, and propagate back values from parsers. */ + { + /* We pass over the groups in reverse order so that child groups are + given a chance to do there processing before passing back a value to + the parent. */ + for (group = parser->egroup - 1 + ; group >= parser->groups && (!err || err == EBADKEY) + ; group--) + err = group_parse (group, &parser->state, ARGP_KEY_SUCCESS, 0); + if (err == EBADKEY) + err = 0; /* Some parser didn't understand. */ + } + + /* Call parsers once more, to do any final cleanup. Errors are ignored. */ + for (group = parser->egroup - 1; group >= parser->groups; group--) + group_parse (group, &parser->state, ARGP_KEY_FINI, 0); + + if (err == EBADKEY) + err = EINVAL; + + free (parser->storage); + + return err; +} + +/* Call the user parsers to parse the non-option argument VAL, at the current + position, returning any error. The state NEXT pointer is assumed to have + been adjusted (by getopt) to point after this argument; this function will + adjust it correctly to reflect however many args actually end up being + consumed. */ +static error_t +parser_parse_arg (struct parser *parser, char *val) +{ + /* Save the starting value of NEXT, first adjusting it so that the arg + we're parsing is again the front of the arg vector. */ + int index = --parser->state.next; + error_t err = EBADKEY; + struct group *group; + int key = 0; /* Which of ARGP_KEY_ARG[S] we used. */ + + /* Try to parse the argument in each parser. */ + for (group = parser->groups + ; group < parser->egroup && err == EBADKEY + ; group++) + { + parser->state.next++; /* For ARGP_KEY_ARG, consume the arg. */ + key = ARGP_KEY_ARG; + err = group_parse (group, &parser->state, key, val); + + if (err == EBADKEY) + /* This parser doesn't like ARGP_KEY_ARG; try ARGP_KEY_ARGS instead. */ + { + parser->state.next--; /* For ARGP_KEY_ARGS, put back the arg. */ + key = ARGP_KEY_ARGS; + err = group_parse (group, &parser->state, key, 0); + } + } + + if (! err) + { + if (key == ARGP_KEY_ARGS) + /* The default for ARGP_KEY_ARGS is to assume that if NEXT isn't + changed by the user, *all* arguments should be considered + consumed. */ + parser->state.next = parser->state.argc; + + if (parser->state.next > index) + /* Remember that we successfully processed a non-option + argument -- but only if the user hasn't gotten tricky and set + the clock back. */ + (--group)->args_processed += (parser->state.next - index); + else + /* The user wants to reparse some args, give getopt another try. */ + parser->try_getopt = 1; + } + + return err; +} + +/* Call the user parsers to parse the option OPT, with argument VAL, at the + current position, returning any error. */ +static error_t +parser_parse_opt (struct parser *parser, int opt, char *val) +{ + /* The group key encoded in the high bits; 0 for short opts or + group_number + 1 for long opts. */ + int group_key = opt >> USER_BITS; + error_t err = EBADKEY; + + if (group_key == 0) + /* A short option. By comparing OPT's position in SHORT_OPTS to the + various starting positions in each group's SHORT_END field, we can + determine which group OPT came from. */ + { + struct group *group; + char *short_index = strchr (parser->short_opts, opt); + + if (short_index) + for (group = parser->groups; group < parser->egroup; group++) + if (group->short_end > short_index) + { + err = group_parse (group, &parser->state, opt, optarg); + break; + } + } + else + /* A long option. We use shifts instead of masking for extracting + the user value in order to preserve the sign. */ + err = + group_parse (&parser->groups[group_key - 1], &parser->state, + (opt << GROUP_BITS) >> GROUP_BITS, optarg); + + if (err == EBADKEY) + /* At least currently, an option not recognized is an error in the + parser, because we pre-compute which parser is supposed to deal + with each option. */ + { + static const char bad_key_err[] = + N_("(PROGRAM ERROR) Option should have been recognized!?"); + if (group_key == 0) + __argp_error (&parser->state, "-%c: %s", opt, + dgettext (parser->argp->argp_domain, bad_key_err)); + else + { + struct option *long_opt = parser->long_opts; + while (long_opt->val != opt && long_opt->name) + long_opt++; + __argp_error (&parser->state, "--%s: %s", + long_opt->name ? long_opt->name : "???", + dgettext (parser->argp->argp_domain, bad_key_err)); + } + } + + return err; +} + +/* Parse the next argument in PARSER (as indicated by PARSER->state.next). + Any error from the parsers is returned, and *ARGP_EBADKEY indicates + whether a value of EBADKEY is due to an unrecognized argument (which is + generally not fatal). */ +static error_t +parser_parse_next (struct parser *parser, int *arg_ebadkey) +{ + int opt; + error_t err = 0; + + if (parser->state.quoted && parser->state.next < parser->state.quoted) + /* The next argument pointer has been moved to before the quoted + region, so pretend we never saw the quoting `--', and give getopt + another chance. If the user hasn't removed it, getopt will just + process it again. */ + parser->state.quoted = 0; + + if (parser->try_getopt && !parser->state.quoted) + /* Give getopt a chance to parse this. */ + { + optind = parser->state.next; /* Put it back in OPTIND for getopt. */ + optopt = KEY_END; /* Distinguish KEY_ERR from a real option. */ + if (parser->state.flags & ARGP_LONG_ONLY) + opt = getopt_long_only (parser->state.argc, parser->state.argv, + parser->short_opts, parser->long_opts, 0); + else + opt = getopt_long (parser->state.argc, parser->state.argv, + parser->short_opts, parser->long_opts, 0); + parser->state.next = optind; /* And see what getopt did. */ + + if (opt == KEY_END) + /* Getopt says there are no more options, so stop using + getopt; we'll continue if necessary on our own. */ + { + parser->try_getopt = 0; + if (parser->state.next > 1 + && strcmp (parser->state.argv[parser->state.next - 1], QUOTE) + == 0) + /* Not only is this the end of the options, but it's a + `quoted' region, which may have args that *look* like + options, so we definitely shouldn't try to use getopt past + here, whatever happens. */ + parser->state.quoted = parser->state.next; + } + else if (opt == KEY_ERR && optopt != KEY_END) + /* KEY_ERR can have the same value as a valid user short + option, but in the case of a real error, getopt sets OPTOPT + to the offending character, which can never be KEY_END. */ + { + *arg_ebadkey = 0; + return EBADKEY; + } + } + else + opt = KEY_END; + + if (opt == KEY_END) + { + /* We're past what getopt considers the options. */ + if (parser->state.next >= parser->state.argc + || (parser->state.flags & ARGP_NO_ARGS)) + /* Indicate that we're done. */ + { + *arg_ebadkey = 1; + return EBADKEY; + } + else + /* A non-option arg; simulate what getopt might have done. */ + { + opt = KEY_ARG; + optarg = parser->state.argv[parser->state.next++]; + } + } + + if (opt == KEY_ARG) + /* A non-option argument; try each parser in turn. */ + err = parser_parse_arg (parser, optarg); + else + err = parser_parse_opt (parser, opt, optarg); + + if (err == EBADKEY) + *arg_ebadkey = (opt == KEY_END || opt == KEY_ARG); + + return err; +} + +/* Parse the options strings in ARGC & ARGV according to the argp in ARGP. + FLAGS is one of the ARGP_ flags above. If END_INDEX is non-NULL, the + index in ARGV of the first unparsed option is returned in it. If an + unknown option is present, EINVAL is returned; if some parser routine + returned a non-zero value, it is returned; otherwise 0 is returned. */ +error_t +__argp_parse (const struct argp *argp, int argc, char **argv, unsigned flags, + int *end_index, void *input) +{ + error_t err; + struct parser parser; + + /* If true, then err == EBADKEY is a result of a non-option argument failing + to be parsed (which in some cases isn't actually an error). */ + int arg_ebadkey = 0; + + // Sripathi: + // Forward declare the struct pointers since malloc is being used instead of alloca + struct argp_child *child; + struct argp *top_argp; + + // Set these to NULL when not used + child = NULL; + top_argp = NULL; + + if (! (flags & ARGP_NO_HELP)) + /* Add our own options. */ + { + // Sripathi: + // Originally alloca was used but since newlib does not implement that + // we are switching over to malloc which is a lot slower + child = (struct argp_child *) malloc (4 * sizeof (struct argp_child)); + top_argp = (struct argp *) malloc (sizeof (struct argp)); + + /* TOP_ARGP has no options, it just serves to group the user & default + argps. */ + memset (top_argp, 0, sizeof (struct argp)); + top_argp->children = child; + + memset (child, 0, 4 * sizeof (struct argp_child)); + + if (argp) + (child++)->argp = argp; + (child++)->argp = &argp_default_argp; + if (argp_program_version || argp_program_version_hook) + (child++)->argp = &argp_version_argp; + child->argp = 0; + + argp = top_argp; + } + + /* Construct a parser for these arguments. */ + err = parser_init (&parser, argp, argc, argv, flags, input); + + if (! err) + /* Parse! */ + { + while (! err) + err = parser_parse_next (&parser, &arg_ebadkey); + err = parser_finalize (&parser, err, arg_ebadkey, end_index); + } + + // Sripathi: + // malloc was used to allocate space for the argp_child and argp structs + // alloca automatically freed the memory when the function returned + // but since we are using malloc, we need to explicitly free it to + // avoid memory leaks + free(top_argp->children); + free(top_argp); + + return err; +} +#ifdef weak_alias +weak_alias (__argp_parse, argp_parse) +#endif + +/* Return the input field for ARGP in the parser corresponding to STATE; used + by the help routines. */ +void * +__argp_input (const struct argp *argp, const struct argp_state *state) +{ + if (state) + { + struct group *group; + struct parser *parser = state->pstate; + + for (group = parser->groups; group < parser->egroup; group++) + if (group->argp == argp) + return group->input; + } + + return 0; +} +#ifdef weak_alias +weak_alias (__argp_input, _argp_input) +#endif diff --git a/libraries/platforms/dromajo-vcs/software/src/argp/argp-pv.c b/libraries/platforms/dromajo-vcs/software/src/argp/argp-pv.c new file mode 100644 index 000000000..5902498e1 --- /dev/null +++ b/libraries/platforms/dromajo-vcs/software/src/argp/argp-pv.c @@ -0,0 +1,54 @@ +// Copyright (c) 2020, University of Washington All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// Redistributions of source code must retain the above copyright notice, this list +// of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above copyright notice, this +// list of conditions and the following disclaimer in the documentation and/or +// other materials provided with the distribution. +// +// Neither the name of the copyright holder nor the names of its contributors may +// be used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// This is a slightly modified copy of the argp library from Newlib + +/* Default definition for ARGP_PROGRAM_VERSION. + Copyright (C) 1996, 1997, 1999 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Written by Miles Bader . + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +/* If set by the user program to a non-zero value, then a default option + --version is added (unless the ARGP_NO_HELP flag is used), which will + print this this string followed by a newline and exit (unless the + ARGP_NO_EXIT flag is used). Overridden by ARGP_PROGRAM_VERSION_HOOK. */ +const char *argp_program_version; diff --git a/libraries/platforms/dromajo-vcs/software/src/argp/argp-pvh.c b/libraries/platforms/dromajo-vcs/software/src/argp/argp-pvh.c new file mode 100644 index 000000000..99d6522de --- /dev/null +++ b/libraries/platforms/dromajo-vcs/software/src/argp/argp-pvh.c @@ -0,0 +1,61 @@ +// Copyright (c) 2020, University of Washington All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// Redistributions of source code must retain the above copyright notice, this list +// of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above copyright notice, this +// list of conditions and the following disclaimer in the documentation and/or +// other materials provided with the distribution. +// +// Neither the name of the copyright holder nor the names of its contributors may +// be used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// This is a slightly modified copy of the argp library from Newlib + +/* Default definition for ARGP_PROGRAM_VERSION_HOOK. + Copyright (C) 1996, 1997, 1999 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Written by Miles Bader . + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +/* If set by the user program to a non-zero value, then a default option + --version is added (unless the ARGP_NO_HELP flag is used), which calls + this function with a stream to print the version to and a pointer to the + current parsing state, and then exits (unless the ARGP_NO_EXIT flag is + used). This variable takes precedent over ARGP_PROGRAM_VERSION. */ +void (*argp_program_version_hook) (FILE *stream, struct argp_state *state); diff --git a/libraries/platforms/dromajo-vcs/software/src/argp/argp-xinl.c b/libraries/platforms/dromajo-vcs/software/src/argp/argp-xinl.c new file mode 100644 index 000000000..61c5a8801 --- /dev/null +++ b/libraries/platforms/dromajo-vcs/software/src/argp/argp-xinl.c @@ -0,0 +1,71 @@ +// Copyright (c) 2020, University of Washington All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// Redistributions of source code must retain the above copyright notice, this list +// of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above copyright notice, this +// list of conditions and the following disclaimer in the documentation and/or +// other materials provided with the distribution. +// +// Neither the name of the copyright holder nor the names of its contributors may +// be used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// This is a slightly modified copy of the argp library from Newlib + +/* Real definitions for extern inline functions in argp.h + Copyright (C) 1997, 1998 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Written by Miles Bader . + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#ifndef __USE_EXTERN_INLINES +# define __USE_EXTERN_INLINES 1 +#endif +#define ARGP_EI +#undef __OPTIMIZE__ +#define __OPTIMIZE__ 1 +#include + +/* Add weak aliases. */ +#if _LIBC - 0 && defined (weak_alias) + +weak_alias (__argp_usage, argp_usage) +weak_alias (__option_is_short, _option_is_short) +weak_alias (__option_is_end, _option_is_end) + +#endif diff --git a/libraries/platforms/dromajo-vcs/software/src/args.c b/libraries/platforms/dromajo-vcs/software/src/args.c new file mode 100644 index 000000000..3081d2ff8 --- /dev/null +++ b/libraries/platforms/dromajo-vcs/software/src/args.c @@ -0,0 +1,129 @@ +// Copyright (c) 2020, University of Washington All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// Redistributions of source code must retain the above copyright notice, this list +// of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above copyright notice, this +// list of conditions and the following disclaimer in the documentation and/or +// other materials provided with the distribution. +// +// Neither the name of the copyright holder nor the names of its contributors may +// be used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// This file includes routines that queries the arguments from the x86 host and +// loads them into corresponding registers so that BlackParrot can access them. + +#include +#include +#include +#include +#include + +// Number of arguments that can be retrieved is currently based on the size of the buffer. +int hb_bp_argc; +char **hb_bp_argv; + +/* + * Retrieve arguments from the host + */ +void hb_bp_init_args(void) { + int err; + hb_mc_packet_t args_req_pkt, args_resp_pkt; + + static int hb_bp_arg_index = 0; + static int hb_bp_char_index = 0; + + // Flat buffer to capture all the arguments contiguously + char buffer[3000]; + + // Create a packet for the x86 host + args_req_pkt.request.x_dst = HOST_X_COORD; + args_req_pkt.request.y_dst = HOST_Y_COORD; + args_req_pkt.request.x_src = BP_HOST_LINK_X; + args_req_pkt.request.y_src = BP_HOST_LINK_Y; + args_req_pkt.request.op_v2 = HB_MC_PACKET_OP_REMOTE_LOAD; + args_req_pkt.request.payload = 0; + args_req_pkt.request.reg_id = 0; + + while(1) { + // The platform setup in simulation ensures that this packet will not go over the network so + // we don't need to check for credits. Also, this code is executed before the CUDA-lite + // program starts. + args_req_pkt.request.addr = HB_BP_HOST_EPA_ARGS_START + (hb_bp_arg_index << 2); + err = hb_bp_write_to_mc_bridge(&args_req_pkt); + err = hb_bp_read_from_mc_bridge(&args_resp_pkt, HB_MC_FIFO_RX_RSP); + if (err != HB_MC_SUCCESS) + bp_finish(err); + + uint32_t data = args_resp_pkt.response.data; + uint8_t null_char = (uint8_t) '\0'; + if (data != HB_BP_HOST_OP_FINISH_CODE) { + uint32_t mask = 0xFF000000; + uint8_t byte; + for(int i = 0; i < 4; i++) { + // Get the correct byte from the 4-byte data and then shift it + // to the lowest byte + byte = ((data & mask) >> (8 * (3 - i))); + + // FIXME: Don't put the first argument (name of the program itself) in the buffer since the loader doesn't expect that + // Fix the loader or argp (more likely)! + if (hb_bp_arg_index != 0) { + if (byte != null_char) { + buffer[hb_bp_char_index++] = (char) byte; + mask >>= 8; + } + else { + buffer[hb_bp_char_index++] = ' '; + hb_bp_arg_index++; + break; + } + } + else { + if (byte == null_char) { + hb_bp_arg_index++; + break; + } + } + } + } + else + break; + } + + // FIXME: Don't put the first argument (name of the program itself) in the buffer since the loader doesn't expect that + // Fix the loader or argp (more likely)! + hb_bp_argc = hb_bp_arg_index - 1; + + // Convert a flat buffer into a 2-D array of pointers as argparse + // expects it to be + char *curr = buffer; + char prev = ' '; + int count = 0; + if (hb_bp_argc != 0) { + while (*curr != '\0') { + if ((prev == ' ') && (prev != *curr)) { + hb_bp_argv[count] = curr; + count++; + } + prev = *curr; + if (*curr == ' ') + *curr = '\0'; + curr++; + } + } +} diff --git a/libraries/platforms/dromajo-vcs/software/src/bp_utils.c b/libraries/platforms/dromajo-vcs/software/src/bp_utils.c new file mode 100644 index 000000000..5d9fee118 --- /dev/null +++ b/libraries/platforms/dromajo-vcs/software/src/bp_utils.c @@ -0,0 +1,147 @@ +// Copyright (c) 2020, University of Washington All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// Redistributions of source code must retain the above copyright notice, this list +// of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above copyright notice, this +// list of conditions and the following disclaimer in the documentation and/or +// other materials provided with the distribution. +// +// Neither the name of the copyright holder nor the names of its contributors may +// be used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// BlackParrot utilities' definitions + +#include +#include + +/* + * Get hardware thread ID + * @returns result of reading the mhartid register + */ +uint64_t bp_get_hart() { + uint64_t core_id; + __asm__ volatile("csrr %0, mhartid": "=r"(core_id): :); + return core_id; +} + +/* + * Checks to see if a barrier is finished + * @param[in] barrier_address --> An address in memory that all cores to write to after hitting the barrier + * @param[in] total_num_cores --> Number of cores + */ +void bp_barrier_end(volatile uint64_t * barrier_address, uint64_t total_num_cores) { + uint64_t core_id; + uint64_t atomic_inc = 1; + uint64_t atomic_result; + __asm__ volatile("csrr %0, mhartid": "=r"(core_id): :); + + /* if we're not core 0, increment the barrier and then just loop */ + if (core_id != 0) { + __asm__ volatile("amoadd.d %0, %2, (%1)": "=r"(atomic_result) + : "r"(barrier_address), "r"(atomic_inc) + :); + while (1) { } + } + /* + * if we're core 0, increment the barrier as well and then test if the + * barrier is equal to the total number of cores + */ + else { + uint64_t finish_value = 0; + __asm__ volatile("amoadd.d %0, %2, (%1)": "=r"(atomic_result) + : "r"(barrier_address), "r"(atomic_inc) + :); + while(*barrier_address < total_num_cores) { + + + } + bp_finish(0); + } +} + +/* + * Sends a finish packet to the host + * @param[in] code --> Finish code + */ +void bp_finish(int16_t code) { + uint64_t core_id; + + // Fixme: Core IDs for all BP cores might be zero. How do we handle this? + __asm__ volatile("csrr %0, mhartid": "=r"(core_id): :); + + // Finish packet contains core id at the top 16 bits and + // finish code at the bottom 16 bits + // zero --> success code + // nonzero --> fail code + hb_mc_packet_t finish_pkt; + finish_pkt.request.x_dst = HOST_X_COORD; + finish_pkt.request.y_dst = HOST_Y_COORD; + finish_pkt.request.x_src = BP_HOST_LINK_X; + finish_pkt.request.y_src = BP_HOST_LINK_Y; + finish_pkt.request.op_v2 = HB_MC_PACKET_OP_REMOTE_SW; + finish_pkt.request.payload = (core_id << 16) | (0x0000FFFF & code); + finish_pkt.request.reg_id = 0; + if (code >= 0) + finish_pkt.request.addr = HB_MC_HOST_EPA_FINISH; + else + finish_pkt.request.addr = HB_MC_HOST_EPA_FAIL; + + int err; + do { + err = hb_bp_write_to_mc_bridge(&finish_pkt); + } while (err != HB_MC_SUCCESS); +} + +/* + * Sends a hex digit to the host to print + * @param[in] hex --> Hex digit to print + */ +void bp_hprint(uint8_t hex) { + + hb_mc_packet_t hprint_pkt; + hprint_pkt.request.x_dst = HOST_X_COORD; + hprint_pkt.request.y_dst = HOST_Y_COORD; + hprint_pkt.request.x_src = BP_HOST_LINK_X; + hprint_pkt.request.y_src = BP_HOST_LINK_Y; + hprint_pkt.request.op_v2 = HB_MC_PACKET_OP_REMOTE_SW; + hprint_pkt.request.payload = ('0' + hex); + hprint_pkt.request.reg_id = 0; + hprint_pkt.request.addr = HB_MC_HOST_EPA_STDOUT; + + int err = hb_bp_write_to_mc_bridge(&hprint_pkt); +} + +/* + * Sends a character to the host to print + * @param[in] ch --> Character to print + */ +void bp_cprint(uint8_t ch) { + + hb_mc_packet_t cprint_pkt; + cprint_pkt.request.x_dst = HOST_X_COORD; + cprint_pkt.request.y_dst = HOST_Y_COORD; + cprint_pkt.request.x_src = BP_HOST_LINK_X; + cprint_pkt.request.y_src = BP_HOST_LINK_Y; + cprint_pkt.request.op_v2 = HB_MC_PACKET_OP_REMOTE_SW; + cprint_pkt.request.payload = ch; + cprint_pkt.request.reg_id = 0; + cprint_pkt.request.addr = HB_MC_HOST_EPA_STDOUT; + + int err = hb_bp_write_to_mc_bridge(&cprint_pkt); +} diff --git a/libraries/platforms/dromajo-vcs/software/src/crt0.S b/libraries/platforms/dromajo-vcs/software/src/crt0.S new file mode 100644 index 000000000..f12d14427 --- /dev/null +++ b/libraries/platforms/dromajo-vcs/software/src/crt0.S @@ -0,0 +1,123 @@ +# Copyright (c) 2020, University of Washington All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# Redistributions of source code must retain the above copyright notice, this list +# of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above copyright notice, this +# list of conditions and the following disclaimer in the documentation and/or +# other materials provided with the distribution. +# +# Neither the name of the copyright holder nor the names of its contributors may +# be used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# Start code + +# This file should be deprecated when the BlackParrot firmware is capable of +# parsing arguments. + +# This assembly file mimics the BlackParrot perch library start.S +# with minor modifications to allow arguments to be loaded into the +# argument registers for main. + +.section ".text.init" +.globl _start +.type _start, @function +_start: +# Setup the global pointer +.option push +.option norelax + la gp, _gp +.option pop + +# Setup stack pointers. +# We then subtract off 8K*coreID. The top 4K is for the core emulation stack +# the lower 4K is for the program + la sp, _sp + csrr t0, mhartid + slli t0, t0, 13 + sub sp, sp, t0 + +# Save the stack pointer to mscratch, so it can be used on first trap entry + csrw mscratch, sp +# Get 4K and subtract it off to get the program stack pointer + li t0, 1 + slli t0, t0, 12 + sub sp, sp, t0 + +# Setup mtvec + la t0, __mtvec_handler + csrw mtvec, t0 + +# Enable FPU + li t0, 0x00006000 & (0x00006000 >> 1); + csrs mstatus, t0; + csrwi fcsr, 0 + +# 0 all registers except sp and gp + addi x1, x0, 0 + addi x4, x0, 0 + addi x5, x0, 0 + addi x6, x0, 0 + addi x7, x0, 0 + addi x8, x0, 0 + addi x9, x0, 0 + addi x10, x0, 0 + addi x11, x0, 0 + addi x12, x0, 0 + addi x13, x0, 0 + addi x14, x0, 0 + addi x15, x0, 0 + addi x16, x0, 0 + addi x17, x0, 0 + addi x18, x0, 0 + addi x19, x0, 0 + addi x20, x0, 0 + addi x21, x0, 0 + addi x22, x0, 0 + addi x23, x0, 0 + addi x24, x0, 0 + addi x25, x0, 0 + addi x26, x0, 0 + addi x27, x0, 0 + addi x28, x0, 0 + addi x29, x0, 0 + addi x30, x0, 0 + addi x31, x0, 0 + + la a0, __libc_fini_array # Register global termination functions + call atexit # to be called upon exit + call __libc_init_array # Run global initialization functions + +#ifdef _DRAMFS + call dramfs_init +#endif + +# Initialize the arguments + call hb_bp_init_args + la a0, hb_bp_argc + ld a0, 0(a0) + la a1, hb_bp_argv + ld a1, 0(a1) + + call main + tail bp_finish + +# Loop if we pass exit +loop: + nop + j loop diff --git a/libraries/platforms/dromajo-vcs/software/src/flockfile.c b/libraries/platforms/dromajo-vcs/software/src/flockfile.c new file mode 100644 index 000000000..57110f6a4 --- /dev/null +++ b/libraries/platforms/dromajo-vcs/software/src/flockfile.c @@ -0,0 +1,41 @@ +// Copyright (c) 2020, University of Washington All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// Redistributions of source code must retain the above copyright notice, this list +// of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above copyright notice, this +// list of conditions and the following disclaimer in the documentation and/or +// other materials provided with the distribution. +// +// Neither the name of the copyright holder nor the names of its contributors may +// be used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// RISC-V Newlib does not seem to define this function anywhere although +// it is declared in stdio.h. +// Since there is no threading library, implement this function as a no-op + +#include + +/** + * Lock a file. This function is currently a no-op since CUDA-lite is not thread-safe. + * @param[in] fp A FILE object pointer + */ +void flockfile(FILE *fp) +{ + // Do nothing +} diff --git a/libraries/platforms/dromajo-vcs/software/src/funlockfile.c b/libraries/platforms/dromajo-vcs/software/src/funlockfile.c new file mode 100644 index 000000000..24d495e96 --- /dev/null +++ b/libraries/platforms/dromajo-vcs/software/src/funlockfile.c @@ -0,0 +1,41 @@ +// Copyright (c) 2020, University of Washington All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// Redistributions of source code must retain the above copyright notice, this list +// of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above copyright notice, this +// list of conditions and the following disclaimer in the documentation and/or +// other materials provided with the distribution. +// +// Neither the name of the copyright holder nor the names of its contributors may +// be used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// RISC-V Newlib does not seem to define this function anywhere although +// it is declared in stdio.h. +// Since there is no threading library, implement this function as a no-op + +#include + +/** + * Unlock a file. This function is currently a no-op since CUDA-lite is not thread-safe. + * @param[in] fp A FILE object pointer + */ +void funlockfile(FILE *fp) +{ + // Do nothing +} diff --git a/machines/.gitignore b/machines/.gitignore index cedb46588..33747ae50 100644 --- a/machines/.gitignore +++ b/machines/.gitignore @@ -16,3 +16,4 @@ bigblade-vcs bsg_tag_boot_rom.v bsg_tag_boot_rom.tr tapeout-vcs +dromajo-vcs diff --git a/platform.mk b/platform.mk index 73d1e8d4e..222f5c9c3 100644 --- a/platform.mk +++ b/platform.mk @@ -80,6 +80,13 @@ $(error $(shell echo -e "$(RED)BSG MAKE ERROR: VERILATOR not defined$(NC)")) endif endif +# If using Dromajo, check if BLACKPARROT_SDK_DIR is defined +ifeq ($(BSG_PLATFORM),dromajo-vcs) +ifndef BLACKPARROT_SDK_DIR +$(error $(shell echo -e "$(RED)BSG MAKE ERROR: BLACKPARROT_SDK_DIR not defined$(NC)")) +endif +endif + # BSG Platform Path is the path to the target platform, i.e. the # substrate that actually runs the machine.