diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml index 7f79bcd7..33b54bb8 100644 --- a/.github/workflows/c-cpp.yml +++ b/.github/workflows/c-cpp.yml @@ -9,9 +9,18 @@ jobs: steps: - name: Install cross compile toolchain - run: sudo apt-get install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf binutils-arm-linux-gnueabihf + run: sudo apt-get install gcc-8-arm-linux-gnueabihf g++-8-arm-linux-gnueabihf binutils-arm-linux-gnueabihf libspdlog-dev - uses: actions/checkout@v2 + - name: dump arm gcc version + run: arm-linux-gnueabihf-gcc -v + working-directory: ./src/raspberrypi + + - name: dump native gcc version + run: gcc -v + working-directory: ./src/raspberrypi + + - name: make standard run: make all DEBUG=1 CONNECT_TYPE=STANDARD working-directory: ./src/raspberrypi diff --git a/doc/rascsi.1 b/doc/rascsi.1 index d08cf868..07ab3e05 100644 --- a/doc/rascsi.1 +++ b/doc/rascsi.1 @@ -57,6 +57,6 @@ To create an empty, 100MB HD image, use the following command: dd if=/dev/zero of=/path/to/newimage.hda bs=512 count=204800 .SH SEE ALSO -rasctl(1), scsidump(1) +rasctl(1), scsimon(1) Full documentation is available at: diff --git a/doc/rascsi_man_page.txt b/doc/rascsi_man_page.txt index d7a26229..de2f98f8 100644 --- a/doc/rascsi_man_page.txt +++ b/doc/rascsi_man_page.txt @@ -2,7 +2,7 @@ !! ------ The native file is rascsi.1. Re-run 'make docs' after updating -rascsi(1) General Commands Manual rascsi(1) +rascsi(1) General Commands Manual rascsi(1) NAME rascsi - Emulates SCSI devices using the Raspberry Pi GPIO pins @@ -13,10 +13,9 @@ SYNOPSIS DESCRIPTION rascsi Emulates SCSI devices using the Raspberry Pi GPIO pins. - In the arguments to RaSCSI, one or more SCSI (-IDn) or SASI (-HDn) devices can be specified. The number (n) - after the ID or HD idnetifier specifies the ID number for that device. For SCSI: The ID is limited from 0-7. - However, typically SCSI ID 7 is reserved for the "initiator" (the host computer).Note that SASI is considered - rare and only used on very early Sharp X68000 computers. + In the arguments to RaSCSI, one or more SCSI (-IDn) or SASI (-HDn) devices can be specified. The number (n) after the ID or HD idnetifier specifies the ID number for that device. For SCSI: + The ID is limited from 0-7. However, typically SCSI ID 7 is reserved for the "initiator" (the host computer).Note that SASI is considered rare and only used on very early Sharp X68000 com‐ + puters. RaSCSI will determin the type of device based upon the file extension of the FILE argument. hdf: SASI Hard Disk image (XM6 SASI HD image - typically only used with X68000) @@ -31,9 +30,8 @@ DESCRIPTION For example, if you want to specify an Apple-compatible HD image on ID 0, you can use the following command: sudo rascsi -ID0 /path/to/drive/hdimage.hda - Once RaSCSI starts, it will open a socket (port 6868) to allow external management commands. If another - process is using port 6868, RaSCSI will terminate, since it is likely another instance of RaSCSI. Once - RaSCSI has initialized, the rasctl utility can be used to send commands. + Once RaSCSI starts, it will open a socket (port 6868) to allow external management commands. If another process is using port 6868, RaSCSI will terminate, since it is likely another in‐ + stance of RaSCSI. Once RaSCSI has initialized, the rasctl utility can be used to send commands. To quit RaSCSI, press Control + C. If it is running in the background, you can kill it using an INT signal. @@ -61,8 +59,8 @@ EXAMPLES dd if=/dev/zero of=/path/to/newimage.hda bs=512 count=204800 SEE ALSO - rasctl(1), scsidump(1) + rasctl(1), scsimon(1) Full documentation is available at: - rascsi(1) + rascsi(1) diff --git a/doc/rasctl.1 b/doc/rasctl.1 index be65b178..1533a182 100644 --- a/doc/rasctl.1 +++ b/doc/rasctl.1 @@ -66,6 +66,6 @@ Request the RaSCSI process to attach a disk (assumed) to SCSI ID 0 with the cont rasctl -i 0 -f HDIIMAGE0.HDS .SH SEE ALSO -rascsi(1) +rascsi(1) scsimon(1) Full documentation is available at: diff --git a/doc/rasctl_man_page.txt b/doc/rasctl_man_page.txt index 9f2d5642..2b0498a8 100644 --- a/doc/rasctl_man_page.txt +++ b/doc/rasctl_man_page.txt @@ -2,7 +2,7 @@ !! ------ The native file is rasctl.1. Re-run 'make docs' after updating -rascsi(1) General Commands Manual rascsi(1) +rascsi(1) General Commands Manual rascsi(1) NAME rasctl - Sends management commands to the rascsi process @@ -11,15 +11,13 @@ SYNOPSIS rasctl -l | -i ID [-u UNIT] [-c CMD] [-t TYPE] [-f FILE] DESCRIPTION - rasctl Sends commands to the rascsi process to make configuration adjustments at runtime or to check the sta‐ - tus of the devices. + rasctl Sends commands to the rascsi process to make configuration adjustments at runtime or to check the status of the devices. Either the -i or -l option should be specified at one time. Not both. You do NOT need root privileges to use rasctl. - Note: The command and type arguments are case insensitive. Only the first letter of the command/type are - evaluated by the tool. + Note: The command and type arguments are case insensitive. Only the first letter of the command/type are evaluated by the tool. OPTIONS -l List all of the devices that are currently being emulated by RaSCSI, as well as their current status. @@ -27,8 +25,7 @@ OPTIONS -i ID ID is the SCSI ID that you want to control. (0-7) -u UNIT - Unit number (0 or 1). This will default to 0. This option is only used when there are multiple SCSI - devices on a shared SCSI controller. (This is not common) + Unit number (0 or 1). This will default to 0. This option is only used when there are multiple SCSI devices on a shared SCSI controller. (This is not common) -c CMD Command is the operation being requested. options are: attach: attach disk @@ -40,8 +37,7 @@ OPTIONS When the command is omited, rasctl will default to the 'attach' command -t TYPE - Specifies the type of disk. If this disagrees with the file extension of the specified image, the TYPE - argument is ignored. Available drive types are: + Specifies the type of disk. If this disagrees with the file extension of the specified image, the TYPE argument is ignored. Available drive types are: hd: Hard disk (SCSI or SASI) mo: Magneto-Optical disk) cd: CD-ROM @@ -61,13 +57,12 @@ EXAMPLES | 0 | 1 | SCHD | /home/pi/harddisk.hda +----+----+------+------------------------------------- - Request the RaSCSI process to attach a disk (assumed) to SCSI ID 0 with the contents of the file system image - "HDIIMAGE0.HDS". + Request the RaSCSI process to attach a disk (assumed) to SCSI ID 0 with the contents of the file system image "HDIIMAGE0.HDS". rasctl -i 0 -f HDIIMAGE0.HDS SEE ALSO - rascsi(1) + rascsi(1) scsimon(1) Full documentation is available at: - rascsi(1) + rascsi(1) diff --git a/doc/scsimon.1 b/doc/scsimon.1 new file mode 100644 index 00000000..670c2103 --- /dev/null +++ b/doc/scsimon.1 @@ -0,0 +1,27 @@ +.TH scsimon 1 +.SH NAME +scsimon \- Acts as a data capture tool for all traffic on the SCSI bus. Data is stored in a Value Change Dump (VCD) file. +.SH SYNOPSIS +.B scsimon +.SH DESCRIPTION +.B scsimon +Monitors all of the traffic on the SCSI bus, using a RaSCSI device. The data is cached in memory while the tool is running. A circular buffer is used so that only the most recent 1,000,000 transactions are stored. The tool will continue to run until the user presses CTRL-C, or the process receives a SIGINT signal. +.PP +The logged data is stored in a file called "log.vcd" in the current working directory from where scsimon was launched. + +Currently, scsimon doesn't accept any agruments. + +To quit scsimon, press Control + C. + +.SH OPTIONS +.TP +None + +.SH EXAMPLES +Launch scsimon to capture all SCSI traffic available to the RaSCSI hardware: + scsimon + +.SH SEE ALSO +rasctl(1), rascsi(1) + +Full documentation is available at: diff --git a/doc/scsimon_man_page.txt b/doc/scsimon_man_page.txt new file mode 100644 index 00000000..0e7db3de --- /dev/null +++ b/doc/scsimon_man_page.txt @@ -0,0 +1,35 @@ +!! ------ THIS FILE IS AUTO_GENERATED! DO NOT MANUALLY UPDATE!!! +!! ------ The native file is scsimon.1. Re-run 'make docs' after updating + + +scsimon(1) General Commands Manual scsimon(1) + +NAME + scsimon - Acts as a data capture tool for all traffic on the SCSI bus. Data is stored in a Value Change Dump (VCD) file. + +SYNOPSIS + scsimon + +DESCRIPTION + scsimon Monitors all of the traffic on the SCSI bus, using a RaSCSI device. The data is cached in memory while the tool is running. A circular buffer is used so that only the most recent 1,000,000 + transactions are stored. The tool will continue to run until the user presses CTRL-C, or the process receives a SIGINT signal. + + The logged data is stored in a file called "log.vcd" in the current working directory from where scsimon was launched. + + Currently, scsimon doesn't accept any agruments. + + To quit scsimon, press Control + C. + +OPTIONS + None + +EXAMPLES + Launch scsimon to capture all SCSI traffic available to the RaSCSI hardware: + scsimon + +SEE ALSO + rasctl(1), rascsi(1) + + Full documentation is available at: + + scsimon(1) diff --git a/src/raspberrypi/.gitignore b/src/raspberrypi/.gitignore index 87d04307..b3172ad3 100644 --- a/src/raspberrypi/.gitignore +++ b/src/raspberrypi/.gitignore @@ -4,6 +4,7 @@ *.cbp *.layout *.log +*.vcd rascsi scsimon rasctl diff --git a/src/raspberrypi/Makefile b/src/raspberrypi/Makefile index 7f119949..da996318 100644 --- a/src/raspberrypi/Makefile +++ b/src/raspberrypi/Makefile @@ -38,8 +38,8 @@ CXXFLAGS += -std=c++14 -iquote . -MD -MP ## STANDARD or FULLSPEC. The default is STANDARD ## * THIS IS TYPICALLY THE ONLY COMPILE OPTION YOU ## * NEED TO SPECIFY -# If its not specified, build for STANDARD configuration -CONNECT_TYPE ?= STANDARD +# If its not specified, build for FULLSPEC configuration +CONNECT_TYPE ?= FULLSPEC ifdef CONNECT_TYPE CFLAGS += -DCONNECT_TYPE_$(CONNECT_TYPE) @@ -67,8 +67,7 @@ BINDIR := ./bin/$(shell echo $(CONNECT_TYPE) | tr '[:upper:]' '[:lower:]') #BIN_ALL = $(RASCSI) $(RASCTL) $(RASDUMP) $(SASIDUMP) $(SCSIMON) # Temporarily remove the RASDUMP and RASDUMP tools, since they're not needed # for my specific use case. If you need them - add them back in! -BIN_ALL = $(BINDIR)/$(RASCSI) $(BINDIR)/$(RASCTL) - +BIN_ALL = $(BINDIR)/$(RASCSI) $(BINDIR)/$(RASCTL) $(BINDIR)/$(SCSIMON) SRC_RASCSI = \ rascsi.cpp \ @@ -83,6 +82,16 @@ SRC_RASCSI = \ SRC_RASCSI += $(shell find ./controllers -name '*.cpp') SRC_RASCSI += $(shell find ./devices -name '*.cpp') +SRC_SCSIMON = \ + scsimon.cpp \ + scsi.cpp \ + gpiobus.cpp \ + filepath.cpp \ + fileio.cpp +SRC_SCSIMON += $(shell find ./controllers -name '*.cpp') +SRC_SCSIMON += $(shell find ./devices -name '*.cpp') + + SRC_RASCTL = \ rasctl.cpp # rasctl_command.cpp @@ -113,12 +122,12 @@ OBJ_RASDUMP := $(addprefix $(OBJDIR)/,$(notdir $(SRC_RASDUMP:%.cpp=%.o))) OBJ_SASIDUMP := $(addprefix $(OBJDIR)/,$(notdir $(SRC_SASIDUMP:%.cpp=%.o))) OBJ_SCSIMON := $(addprefix $(OBJDIR)/,$(notdir $(SRC_SCSIMON:%.cpp=%.o))) #OBJ_ALL := $(OBJ_RASCSI) $(OBJ_RASCTL) $(OBJ_RASDUMP) $(OBJ_SASIDUMP) $(OBJ_SCSIMON) -OBJ_ALL := $(OBJ_RASCSI) $(OBJ_RASCTL) $(OBJ_RASDUMP) $(OBJ_SASIDUMP) +OBJ_ALL := $(OBJ_RASCSI) $(OBJ_RASCTL) $(OBJ_RASDUMP) $(OBJ_SASIDUMP) $(OBJ_SCSIMON) # The following will include all of the auto-generated dependency files (*.d) # if they exist. This will trigger a rebuild of a source file if a header changes -ALL_DEPS := $(patsubst %.o,%.d,$(OBJ_RASCSI) $(OBJ_RASCTL)) +ALL_DEPS := $(patsubst %.o,%.d,$(OBJ_RASCSI) $(OBJ_RASCTL) $(OBJ_SCSIMON)) -include $(ALL_DEPS) $(OBJDIR) $(BINDIR): @@ -137,21 +146,21 @@ $(OBJDIR)/%.o: %.cpp | $(OBJDIR) all: $(BIN_ALL) docs ALL: all -docs: $(DOC_DIR)/rascsi_man_page.txt $(DOC_DIR)/rasctl_man_page.txt +docs: $(DOC_DIR)/rascsi_man_page.txt $(DOC_DIR)/rasctl_man_page.txt $(DOC_DIR)/scsimon_man_page.txt $(BINDIR)/$(RASCSI): $(OBJ_RASCSI) | $(BINDIR) - $(CXX) -o $@ $(OBJ_RASCSI) -lpthread + $(CXX) $(CXXFLAGS) -o $@ $(OBJ_RASCSI) -lpthread -$(BINDIR)/$(RASCTL): $(OBJ_RASCTL) $(BINDIR) - $(CXX) -o $@ $(OBJ_RASCTL) +$(BINDIR)/$(RASCTL): $(OBJ_RASCTL) | $(BINDIR) + $(CXX) $(CXXFLAGS) -o $@ $(OBJ_RASCTL) -$(RASDUMP): $(OBJ_RASDUMP) $(BINDIR) - $(CXX) -o $@ $(OBJ_RASDUMP) +$(BINDIR)/$(RASDUMP): $(OBJ_RASDUMP) | $(BINDIR) + $(CXX) $(CXXFLAGS) -o $@ $(OBJ_RASDUMP) -$(SASIDUMP): $(OBJ_SASIDUMP) $(BINDIR) - $(CXX) -o $@ $(OBJ_SASIDUMP) +$(BINDIR)/$(SASIDUMP): $(OBJ_SASIDUMP) | $(BINDIR) + $(CXX) $(CXXFLAGS) -o $@ $(OBJ_SASIDUMP) -$(SCSIMON): $(OBJ_SCSIMON) $(BINDIR) +$(BINDIR)/$(SCSIMON): $(OBJ_SCSIMON) | $(BINDIR) $(CXX) $(CXXFLAGS) -o $@ $(OBJ_SCSIMON) -lpthread ## clean : Remove all of the object files, intermediate @@ -180,7 +189,7 @@ run: ## * sudo systemctl enable rascsi ## * sudo systemctl start rascsi .PHONY: install -install: $(MAN_PAGE_DIR)/rascsi.1 $(MAN_PAGE_DIR)/rasctl.1 $(USR_LOCAL_BIN)/$(RASCTL) $(USR_LOCAL_BIN)/$(RASCSI) $(SYSTEMD_CONF) $(RSYSLOG_CONF) $(RSYSLOG_LOG) +install: $(MAN_PAGE_DIR)/rascsi.1 $(MAN_PAGE_DIR)/rasctl.1 $(MAN_PAGE_DIR)/scsimon.1 $(USR_LOCAL_BIN)/$(RASCTL) $(USR_LOCAL_BIN)/$(RASCSI) $(USR_LOCAL_BIN)/$(SCSIMON) $(SYSTEMD_CONF) $(RSYSLOG_CONF) $(RSYSLOG_LOG) @echo "-- Done installing!" $(USR_LOCAL_BIN)% : $(BINDIR)/% diff --git a/src/raspberrypi/controllers/sasidev_ctrl.cpp b/src/raspberrypi/controllers/sasidev_ctrl.cpp index 1f97d867..0cd1f420 100644 --- a/src/raspberrypi/controllers/sasidev_ctrl.cpp +++ b/src/raspberrypi/controllers/sasidev_ctrl.cpp @@ -296,7 +296,7 @@ BUS::phase_t FASTCALL SASIDEV::Process() } // Get bus information - ctrl.bus->Aquire(); + ((GPIOBUS*)ctrl.bus)->Aquire(); // For the monitor tool, we shouldn't need to reset. We're just logging information // Reset @@ -927,7 +927,7 @@ void FASTCALL SASIDEV::Error() ASSERT(this); // Get bus information - ctrl.bus->Aquire(); + ((GPIOBUS*)ctrl.bus)->Aquire(); // Reset check if (ctrl.bus->GetRST()) { diff --git a/src/raspberrypi/controllers/scsidev_ctrl.cpp b/src/raspberrypi/controllers/scsidev_ctrl.cpp index c6813c67..db237096 100644 --- a/src/raspberrypi/controllers/scsidev_ctrl.cpp +++ b/src/raspberrypi/controllers/scsidev_ctrl.cpp @@ -76,7 +76,7 @@ BUS::phase_t FASTCALL SCSIDEV::Process() } // Get bus information - ctrl.bus->Aquire(); + ((GPIOBUS*)ctrl.bus)->Aquire(); // Reset if (ctrl.bus->GetRST()) { @@ -488,7 +488,7 @@ void FASTCALL SCSIDEV::Error() ASSERT(this); // Get bus information - ctrl.bus->Aquire(); + ((GPIOBUS*)ctrl.bus)->Aquire(); // Reset check if (ctrl.bus->GetRST()) { diff --git a/src/raspberrypi/gpiobus.cpp b/src/raspberrypi/gpiobus.cpp index af62d171..6996bcb3 100644 --- a/src/raspberrypi/gpiobus.cpp +++ b/src/raspberrypi/gpiobus.cpp @@ -15,6 +15,7 @@ #include "os.h" #include "xm6.h" #include "gpiobus.h" +#include "log.h" #ifndef BAREMETAL #ifdef __linux__ @@ -65,7 +66,7 @@ DWORD bcm_host_get_peripheral_address(void) char buf[1024]; size_t len = sizeof(buf); DWORD address; - + if (sysctlbyname("hw.model", buf, &len, NULL, 0) || strstr(buf, "ARM1176JZ-S") != buf) { // Failed to get CPU model || Not BCM2835 @@ -88,7 +89,7 @@ extern uint32_t RPi_IO_Base_Addr; // Core frequency extern uint32_t RPi_Core_Freq; -#ifdef USE_SEL_EVENT_ENABLE +#ifdef USE_SEL_EVENT_ENABLE //--------------------------------------------------------------------------- // // Interrupt control function @@ -173,7 +174,7 @@ BOOL FASTCALL GPIOBUS::Init(mode_e mode) // Open /dev/mem fd = open("/dev/mem", O_RDWR | O_SYNC); if (fd == -1) { - printf("Error: Unable to open /dev/mem. Are you running as root?\n"); + LOGERROR("Error: Unable to open /dev/mem. Are you running as root?"); return FALSE; } @@ -295,6 +296,7 @@ BOOL FASTCALL GPIOBUS::Init(mode_e mode) // GPIO chip open fd = open("/dev/gpiochip0", 0); if (fd == -1) { + LOGERROR("Unable to open /dev/gpiochip0. Is RaSCSI already running?") return FALSE; } @@ -310,6 +312,7 @@ BOOL FASTCALL GPIOBUS::Init(mode_e mode) //Get event request if (ioctl(fd, GPIO_GET_LINEEVENT_IOCTL, &selevreq) == -1) { + LOGERROR("Unable to register event request. Is RaSCSI already running?") close(fd); return FALSE; } @@ -522,27 +525,6 @@ void FASTCALL GPIOBUS::Reset() #endif // ifdef __x86_64__ || __X86__ } -//--------------------------------------------------------------------------- -// -// Bus signal acquisition -// -//--------------------------------------------------------------------------- -DWORD FASTCALL GPIOBUS::Aquire() -{ -#if defined(__x86_64__) || defined(__X86__) - return 0; -#else - signals = *level; - -#if SIGNAL_CONTROL_MODE < 2 - // Invert if negative logic (internal processing is unified to positive logic) - signals = ~signals; -#endif // SIGNAL_CONTROL_MODE - - return signals; -#endif // ifdef __x86_64__ || __X86__ -} - //--------------------------------------------------------------------------- // // ENB signal setting @@ -668,6 +650,26 @@ void FASTCALL GPIOBUS::SetACK(BOOL ast) SetSignal(PIN_ACK, ast); } +//--------------------------------------------------------------------------- +// +// Get ACK signal +// +//--------------------------------------------------------------------------- +BOOL FASTCALL GPIOBUS::GetACT() +{ + return GetSignal(PIN_ACT); +} + +//--------------------------------------------------------------------------- +// +// Set ACK signal +// +//--------------------------------------------------------------------------- +void FASTCALL GPIOBUS::SetACT(BOOL ast) +{ + SetSignal(PIN_ACT, ast); +} + //--------------------------------------------------------------------------- // // Get RST signal @@ -1173,7 +1175,7 @@ int FASTCALL GPIOBUS::SendHandShake(BYTE *buf, int count) } // Already waiting for REQ assertion - + // Assert the ACK signal SetSignal(PIN_ACK, ON); @@ -1233,7 +1235,7 @@ int FASTCALL GPIOBUS::PollSelectEvent() return -1; } - read(selevreq.fd, &gpev, sizeof(gpev)); + (void)read(selevreq.fd, &gpev, sizeof(gpev)); #endif // BAREMETAL return 0; @@ -1417,7 +1419,7 @@ void FASTCALL GPIOBUS::SetMode(int pin, int mode) gpio[index] = data; gpfsel[index] = data; } - + //--------------------------------------------------------------------------- // // Get input signal value @@ -1427,7 +1429,7 @@ BOOL FASTCALL GPIOBUS::GetSignal(int pin) { return (signals >> pin) & 1; } - + //--------------------------------------------------------------------------- // // Set output signal value @@ -1645,6 +1647,39 @@ void FASTCALL GPIOBUS::DrvConfig(DWORD drive) pads[PAD_0_27] = (0xFFFFFFF8 & data) | drive | 0x5a000000; } + +//--------------------------------------------------------------------------- +// +// Generic Phase Acquisition (Doesn't read GPIO) +// +//--------------------------------------------------------------------------- +BUS::phase_t FASTCALL GPIOBUS::GetPhaseRaw(DWORD raw_data) +{ + DWORD mci; + + // Selection Phase + if (GetPinRaw(raw_data, PIN_SEL)) + { + if(GetPinRaw(raw_data, PIN_IO)){ + return BUS::reselection; + }else{ + return BUS::selection; + } + } + + // Bus busy phase + if (!GetPinRaw(raw_data, PIN_BSY)) { + return BUS::busfree; + } + + // Get target phase from bus signal line + mci = GetPinRaw(raw_data, PIN_MSG) ? 0x04 : 0x00; + mci |= GetPinRaw(raw_data, PIN_CD) ? 0x02 : 0x00; + mci |= GetPinRaw(raw_data, PIN_IO) ? 0x01 : 0x00; + return GetPhase(mci); +} + + //--------------------------------------------------------------------------- // // System timer address diff --git a/src/raspberrypi/gpiobus.h b/src/raspberrypi/gpiobus.h index 340c9585..0bce816b 100644 --- a/src/raspberrypi/gpiobus.h +++ b/src/raspberrypi/gpiobus.h @@ -276,6 +276,26 @@ #define PIN_SEL 23 // SEL #endif +#define ALL_SCSI_PINS \ + ((1<> pin_num) & 1); + } virtual BOOL FASTCALL GetBSY() = 0; // BSYシグナル取得 @@ -125,6 +131,8 @@ public: private: static const phase_t phase_table[8]; // フェーズテーブル + + static const char* phase_str_table[]; }; #endif // scsi_h diff --git a/src/raspberrypi/scsimon.cpp b/src/raspberrypi/scsimon.cpp new file mode 100644 index 00000000..1f551d24 --- /dev/null +++ b/src/raspberrypi/scsimon.cpp @@ -0,0 +1,420 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) +// for Raspberry Pi +// +// Powered by XM6 TypeG Technology. +// Copyright (C) 2016-2020 GIMONS +// [ RaSCSI main ] +// +//--------------------------------------------------------------------------- + +#include "os.h" +#include "xm6.h" +#include "filepath.h" +#include "fileio.h" +#include "devices/disk.h" +#include "log.h" +#include "gpiobus.h" +#include "spdlog/spdlog.h" +#include + +//--------------------------------------------------------------------------- +// +// Constant declarations +// +//--------------------------------------------------------------------------- +#define MAX_BUFF_SIZE 1000000 + +// Symbol definition for the VCD file +// These are just arbitrary symbols. They can be anything allowed by the VCD file format, +// as long as they're consistently used. +#define SYMBOL_PIN_DAT '#' +#define SYMBOL_PIN_ATN '+' +#define SYMBOL_PIN_RST '$' +#define SYMBOL_PIN_ACK '%' +#define SYMBOL_PIN_REQ '^' +#define SYMBOL_PIN_MSG '&' +#define SYMBOL_PIN_CD '*' +#define SYMBOL_PIN_IO '(' +#define SYMBOL_PIN_BSY ')' +#define SYMBOL_PIN_SEL '-' +#define SYMBOL_PIN_PHASE '=' + +// We'll use position 0 in the prev_value array to store the previous phase +#define PIN_PHASE 0 + +//--------------------------------------------------------------------------- +// +// Variable declarations +// +//--------------------------------------------------------------------------- +static BYTE prev_value[32] = {0xFF}; +static volatile BOOL running; // Running flag +static volatile BOOL active; // Processing flag +GPIOBUS *bus; // GPIO Bus +typedef struct data_capture{ + DWORD data; + timeval timestamp; +} data_capture_t; + +data_capture data_buffer[MAX_BUFF_SIZE]; +int data_idx = 0; + +//--------------------------------------------------------------------------- +// +// Signal Processing +// +//--------------------------------------------------------------------------- +void KillHandler(int sig) +{ + // Stop instruction + running = FALSE; +} + +//--------------------------------------------------------------------------- +// +// Banner Output +// +//--------------------------------------------------------------------------- +void Banner(int argc, char* argv[]) +{ + LOGINFO("SCSI Monitor Capture Tool - part of RaSCSI(*^..^*) "); + LOGINFO("version %01d.%01d%01d(%s, %s)", + (int)((VERSION >> 8) & 0xf), + (int)((VERSION >> 4) & 0xf), + (int)((VERSION ) & 0xf), + __DATE__, + __TIME__); + LOGINFO("Powered by XM6 TypeG Technology "); + LOGINFO("Copyright (C) 2016-2020 GIMONS"); + LOGINFO("Copyright (C) 2020 akuker"); + LOGINFO("Connect type : %s", CONNECT_DESC); + LOGINFO(" log.vcd - Value Change Dump file that can be opened with GTKWave"); + + if ((argc > 1 && strcmp(argv[1], "-h") == 0) || + (argc > 1 && strcmp(argv[1], "--help") == 0)){ + LOGINFO("Usage: %s ...", argv[0]); + exit(0); + } + else + { + LOGINFO(" "); + LOGINFO("Current running & collecting data. Press CTRL-C to stop.") + LOGINFO(" "); + } +} + +//--------------------------------------------------------------------------- +// +// Initialization +// +//--------------------------------------------------------------------------- +BOOL Init() +{ + // Interrupt handler settings + if (signal(SIGINT, KillHandler) == SIG_ERR) { + return FALSE; + } + if (signal(SIGHUP, KillHandler) == SIG_ERR) { + return FALSE; + } + if (signal(SIGTERM, KillHandler) == SIG_ERR) { + return FALSE; + } + + // GPIOBUS creation + bus = new GPIOBUS(); + + // GPIO Initialization + if (!bus->Init()) { + LOGERROR("Unable to intiailize the GPIO bus. Exiting...."); + return FALSE; + } + + // Bus Reset + bus->Reset(); + + // Other + running = FALSE; + active = FALSE; + + return TRUE; +} + +BOOL get_pin_value(DWORD data, int pin) +{ + return (data >> pin) & 1; +} + +BYTE get_data_field(DWORD data) +{ + DWORD data_out; + data_out = + ((data >> (PIN_DT0 - 0)) & (1 << 0)) | + ((data >> (PIN_DT1 - 1)) & (1 << 1)) | + ((data >> (PIN_DT2 - 2)) & (1 << 2)) | + ((data >> (PIN_DT3 - 3)) & (1 << 3)) | + ((data >> (PIN_DT4 - 4)) & (1 << 4)) | + ((data >> (PIN_DT5 - 5)) & (1 << 5)) | + ((data >> (PIN_DT6 - 6)) & (1 << 6)) | + ((data >> (PIN_DT7 - 7)) & (1 << 7)); + + return (BYTE)data_out; +} + +void vcd_output_if_changed_phase(FILE *fp, DWORD data, int pin, char symbol) +{ + BUS::phase_t new_value = GPIOBUS::GetPhaseRaw(data); + if(prev_value[pin] != new_value) + { + prev_value[pin] = new_value; + fprintf(fp, "s%s %c\n", GPIOBUS::GetPhaseStrRaw(new_value), symbol); + } +} + +void vcd_output_if_changed_bool(FILE *fp, DWORD data, int pin, char symbol) +{ + BOOL new_value = get_pin_value(data,pin); + if(prev_value[pin] != new_value) + { + prev_value[pin] = new_value; + fprintf(fp, "%d%c\n", new_value, symbol); + } +} + +void vcd_output_if_changed_byte(FILE *fp, DWORD data, int pin, char symbol) +{ + BYTE new_value = get_data_field(data); + if(prev_value[pin] != new_value) + { + prev_value[pin] = new_value; + fprintf(fp, "b%d%d%d%d%d%d%d%d %c\n", + get_pin_value(data,PIN_DT0), + get_pin_value(data,PIN_DT1), + get_pin_value(data,PIN_DT2), + get_pin_value(data,PIN_DT3), + get_pin_value(data,PIN_DT4), + get_pin_value(data,PIN_DT5), + get_pin_value(data,PIN_DT6), + get_pin_value(data,PIN_DT7), symbol); + } +} + + + +void create_value_change_dump() +{ + time_t rawtime; + struct tm * timeinfo; + int i = 0; + timeval time_diff; + char timestamp[256]; + FILE *fp; + timeval start_time = data_buffer[0].timestamp; + LOGINFO("Creating Value Change Dump file (log.vcd)\n"); + fp = fopen("log.vcd","w"); + + // Get the current time + time (&rawtime); + timeinfo = localtime(&rawtime); + strftime (timestamp,sizeof(timestamp),"%d-%m-%Y %H-%M-%S",timeinfo); + + fprintf(fp, "$date\n"); + fprintf(fp, "%s\n", timestamp); + fprintf(fp, "$end\n"); + fprintf(fp, "$version\n"); + fprintf(fp, " VCD generator tool version info text.\n"); + fprintf(fp, "$end\n"); + fprintf(fp, "$comment\n"); + fprintf(fp, " Any comment text.\n"); + fprintf(fp, "$end\n"); + fprintf(fp, "$timescale 1 us $end\n"); + fprintf(fp, "$scope module logic $end\n"); + fprintf(fp, "$var wire 1 %c BSY $end\n", SYMBOL_PIN_BSY); + fprintf(fp, "$var wire 1 %c SEL $end\n", SYMBOL_PIN_SEL); + fprintf(fp, "$var wire 1 %c CD $end\n", SYMBOL_PIN_CD); + fprintf(fp, "$var wire 1 %c IO $end\n", SYMBOL_PIN_IO); + fprintf(fp, "$var wire 1 %c MSG $end\n", SYMBOL_PIN_MSG); + fprintf(fp, "$var wire 1 %c REQ $end\n", SYMBOL_PIN_REQ); + fprintf(fp, "$var wire 1 %c ACK $end\n", SYMBOL_PIN_ACK); + fprintf(fp, "$var wire 1 %c ATN $end\n", SYMBOL_PIN_ATN); + fprintf(fp, "$var wire 1 %c RST $end\n", SYMBOL_PIN_RST); + fprintf(fp, "$var wire 8 %c data $end\n", SYMBOL_PIN_DAT); + fprintf(fp, "$var string 1 %c phase $end\n", SYMBOL_PIN_PHASE); + fprintf(fp, "$upscope $end\n"); + fprintf(fp, "$enddefinitions $end\n"); + + // Initial values - default to zeros + fprintf(fp, "$dumpvars\n"); + fprintf(fp, "0%c\n", SYMBOL_PIN_BSY); + fprintf(fp, "0%c\n", SYMBOL_PIN_SEL); + fprintf(fp, "0%c\n", SYMBOL_PIN_CD); + fprintf(fp, "0%c\n", SYMBOL_PIN_IO); + fprintf(fp, "0%c\n", SYMBOL_PIN_MSG); + fprintf(fp, "0%c\n", SYMBOL_PIN_REQ); + fprintf(fp, "0%c\n", SYMBOL_PIN_ACK); + fprintf(fp, "0%c\n", SYMBOL_PIN_ATN); + fprintf(fp, "0%c\n", SYMBOL_PIN_RST); + fprintf(fp, "b00000000 %c\n", SYMBOL_PIN_DAT); + fprintf(fp, "$end\n"); + + while(i < data_idx) + { + timersub(&(data_buffer[i].timestamp), &start_time, &time_diff); + fprintf(fp, "#%ld\n",((time_diff.tv_sec*1000000) + time_diff.tv_usec)); + vcd_output_if_changed_bool(fp, data_buffer[i].data, PIN_BSY, SYMBOL_PIN_BSY); + vcd_output_if_changed_bool(fp, data_buffer[i].data, PIN_SEL, SYMBOL_PIN_SEL); + vcd_output_if_changed_bool(fp, data_buffer[i].data, PIN_CD, SYMBOL_PIN_CD); + vcd_output_if_changed_bool(fp, data_buffer[i].data, PIN_IO, SYMBOL_PIN_IO); + vcd_output_if_changed_bool(fp, data_buffer[i].data, PIN_MSG, SYMBOL_PIN_MSG); + vcd_output_if_changed_bool(fp, data_buffer[i].data, PIN_REQ, SYMBOL_PIN_REQ); + vcd_output_if_changed_bool(fp, data_buffer[i].data, PIN_ACK, SYMBOL_PIN_ACK); + vcd_output_if_changed_bool(fp, data_buffer[i].data, PIN_ATN, SYMBOL_PIN_ATN); + vcd_output_if_changed_bool(fp, data_buffer[i].data, PIN_RST, SYMBOL_PIN_RST); + vcd_output_if_changed_byte(fp, data_buffer[i].data, PIN_DT0, SYMBOL_PIN_DAT); + vcd_output_if_changed_phase(fp, data_buffer[i].data, PIN_PHASE, SYMBOL_PIN_PHASE); + i++; + } + fclose(fp); +} + + + +//--------------------------------------------------------------------------- +// +// Cleanup +// +//--------------------------------------------------------------------------- +void Cleanup() +{ + + LOGINFO("Stoping data collection....\n"); + create_value_change_dump(); + + // Cleanup the Bus + bus->Cleanup(); + + // Discard the GPIOBUS object + delete bus; + +} + +//--------------------------------------------------------------------------- +// +// Reset +// +//--------------------------------------------------------------------------- +void Reset() +{ + // Reset the bus + bus->Reset(); +} + +//--------------------------------------------------------------------------- +// +// Pin the thread to a specific CPU +// +//--------------------------------------------------------------------------- +void FixCpu(int cpu) +{ + cpu_set_t cpuset; + int cpus; + + // Get the number of CPUs + CPU_ZERO(&cpuset); + sched_getaffinity(0, sizeof(cpu_set_t), &cpuset); + cpus = CPU_COUNT(&cpuset); + + // Set the thread affinity + if (cpu < cpus) { + CPU_ZERO(&cpuset); + CPU_SET(cpu, &cpuset); + sched_setaffinity(0, sizeof(cpu_set_t), &cpuset); + } +} + +#ifdef DEBUG +static DWORD high_bits = 0x0; +static DWORD low_bits = 0xFFFFFFFF; +#endif + +//--------------------------------------------------------------------------- +// +// Main processing +// +//--------------------------------------------------------------------------- +int main(int argc, char* argv[]) +{ +#ifdef DEBUG + DWORD prev_high = high_bits; + DWORD prev_low = low_bits; +#endif + DWORD prev_sample = 0xFFFFFFFF; + DWORD this_sample = 0; + int ret; + struct sched_param schparam; + + spdlog::set_level(spdlog::level::trace); + spdlog::set_pattern("%^[%l]%$ %v"); + // Output the Banner + Banner(argc, argv); + memset(data_buffer,0,sizeof(data_buffer)); + + // Initialize + ret = 0; + if (!Init()) { + ret = EPERM; + goto init_exit; + } + + // Reset + Reset(); + + // Set the affinity to a specific processor core + FixCpu(3); + + // Scheduling policy setting (highest priority) + schparam.sched_priority = sched_get_priority_max(SCHED_FIFO); + sched_setscheduler(0, SCHED_FIFO, &schparam); + + // Start execution + running = TRUE; + bus->SetACT(FALSE); + + LOGDEBUG("ALL_SCSI_PINS %08X\n",ALL_SCSI_PINS); + // Main Loop + while (running) { + // Work initialization + this_sample = (bus->Aquire() & ALL_SCSI_PINS); + + if(this_sample != prev_sample) + { + +#ifdef DEBUG + // This is intended to be a debug check to see if every pin is set + // high and low at some point. + high_bits |= this_sample; + low_bits &= this_sample; + if ((high_bits != prev_high) || (low_bits != prev_low)) + { + LOGDEBUG(" %08lX %08lX\n",high_bits, low_bits); + } + prev_high = high_bits; + prev_low = low_bits; +#endif + data_buffer[data_idx].data = this_sample; + (void)gettimeofday(&(data_buffer[data_idx].timestamp), NULL); + data_idx = (data_idx + 1) % MAX_BUFF_SIZE; + prev_sample = this_sample; + } + + continue; + } + + // Cleanup + Cleanup(); + +init_exit: + exit(ret); +}