diff --git a/servers/fpga_server_lin/src/Makefile.am b/servers/fpga_server_lin/src/Makefile.am index 0176fbe..0cf3322 100644 --- a/servers/fpga_server_lin/src/Makefile.am +++ b/servers/fpga_server_lin/src/Makefile.am @@ -3,7 +3,7 @@ KDE_CXXFLAGS = $(USE_EXCEPTIONS) bin_PROGRAMS = ulab_fpgaserver -ulab_fpgaserver_SOURCES = main.cpp fpga_conn.cpp +ulab_fpgaserver_SOURCES = main.cpp fpga_conn.cpp bbb-gpmc-init.cpp ulab_fpgaserver_METASOURCES = AUTO ulab_fpgaserver_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(LIB_QT) -lDCOP $(LIB_TDECORE) $(LIB_TDEUI) -ltdefx $(LIB_TDEIO) -ltdetexteditor -ltdekrbsocket -ltqtrla diff --git a/servers/fpga_server_lin/src/bbb-gpmc-init.cpp b/servers/fpga_server_lin/src/bbb-gpmc-init.cpp new file mode 100644 index 0000000..961c560 --- /dev/null +++ b/servers/fpga_server_lin/src/bbb-gpmc-init.cpp @@ -0,0 +1,181 @@ +/* + * Remote Laboratory FPGA Server GPMC Interface (Beaglebone Black) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * (c) 2012-2014 Timothy Pearson + * Raptor Engineering + * http://www.raptorengineeringinc.com + */ + +/** BEGIN: Low-Level I/O Implementation **/ + +// Beaglebone Black GPMC driver + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define MEMORY_SPACE_ADDRESS_BITS 16 + +#define GPMC_BASE 0x50000000 +#define GPMC_REGLEN 0x10000000 + +#define GPMC_CHIPSELECTCONFIGDISPLACEMENT (0x30 / 4) + +#define GPMC_CONFIG (0x50 / 4) +#define GPMC_CONFIG1 (0x60 / 4) +#define GPMC_CONFIG2 (0x64 / 4) +#define GPMC_CONFIG3 (0x68 / 4) +#define GPMC_CONFIG4 (0x6c / 4) +#define GPMC_CONFIG5 (0x70 / 4) +#define GPMC_CONFIG6 (0x74 / 4) +#define GPMC_CONFIG7 (0x78 / 4) + +#define MEMORY_SIZE (1 << MEMORY_SPACE_ADDRESS_BITS) + +int mem_fd = 0; +int gpmc_mem_fd = 0; +char *gpio_mem, *gpio_map, *gpmc_map; + +// I/O access +volatile unsigned int *gpio = NULL; +volatile unsigned char *gpio_char = NULL; +volatile unsigned int *gpmc = NULL; + +void gpmc_mapregisters() { + /* open /dev/mem */ + if ((gpmc_mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) { + printf("[FATAL] can't open /dev/mem\n"); + return; + } + + /* mmap GPMC */ + gpmc_map = (char *)mmap( + 0, + GPMC_REGLEN, + PROT_READ|PROT_WRITE, + MAP_SHARED, + gpmc_mem_fd, + GPMC_BASE + ); + + if (gpmc_map == MAP_FAILED) { + printf("[FATAL] mmap error %d\n", (int)gpmc_map); + return; + } + + // Always use volatile pointer! + gpmc = (volatile unsigned *)gpmc_map; +} + +void gpmc_unmapregisters() { + munmap((void*) gpmc, GPMC_REGLEN); + if (gpmc_mem_fd != -1) { + close(gpmc_mem_fd); + } +} + +void gpmc_setup(void) { + gpmc_mapregisters(); + + if (gpmc != NULL) { + int chipselect = 0; + int displacement = GPMC_CHIPSELECTCONFIGDISPLACEMENT * chipselect; + + // disable before playing with the registers + *(gpmc + displacement + GPMC_CONFIG7) = 0x00000000; + + *(gpmc + displacement + GPMC_CONFIG) = 0x00000000; // Unlimited address space + *(gpmc + displacement + GPMC_CONFIG1) = 0x00000000; // No burst, async, 8-bit, non multiplexed + *(gpmc + displacement + GPMC_CONFIG2) = 0x00001000; // Assert CS on fclk0, deassert CS on fclk16 + *(gpmc + displacement + GPMC_CONFIG3) = 0x00000400; // Assert ADV on fclk 0, deassert ADV on fclk 4 + *(gpmc + displacement + GPMC_CONFIG4) = 0x0c041004; // Assert WE on fclk4, deassert WE on fclk12, assert OE on fclk4, deassert OE on fclk16 + *(gpmc + displacement + GPMC_CONFIG5) = 0x000c1010; // Data valid on fclk 12, cycle time 16 fclks + *(gpmc + displacement + GPMC_CONFIG6) = 0x00000000; // No back to back cycle restrictions + *(gpmc + displacement + GPMC_CONFIG7) = 0x00000e50; // CS0: Set base address 0x10000000, 32MB region, and enable CS + + gpmc_unmapregisters(); + } +} + +int setup_gpmc_bbb(void) { + /* open /dev/mem */ + if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) { + printf("[FATAL] can't open /dev/mem\n"); + return -1; + } + + /* mmap GPIO */ + gpio_map = (char *)mmap( + 0, + MEMORY_SIZE, + PROT_READ|PROT_WRITE, + MAP_SHARED, + mem_fd, + 0x10000000 + ); + + if (gpio_map == MAP_FAILED) { + printf("[FATAL] mmap error %d\n", (int)gpio_map); + return -1; + } + + // Always use volatile pointers! + gpio = (volatile unsigned *)gpio_map; + gpio_char = (volatile unsigned char *)gpio_map; + + return 0; +} + +int shutdown_gpmc_bbb(void) { + return 0; +} + +void write_gpmc(unsigned int register_offset, unsigned char data) { + *(gpio_char + register_offset) = data; +} + +unsigned char read_gpmc(unsigned int register_offset) { + return *(gpio_char + register_offset); +} + +void memcpy_from_gpmc(char* destination, unsigned int register_offset, unsigned int length) { + unsigned int i; + for (i=0; i #include #include +#include #include @@ -42,6 +43,8 @@ #include "fpga_conn.h" +#include "bbb-gpmc-init.h" + #define FLUSH_IN 0 #define FLUSH_OUT 1 #define FLUSH_BOTH 2 @@ -137,9 +140,29 @@ void FPGASocket::finishKerberosHandshake() { close(); return; } - if (setupSerial() != 0) { + m_config->setGroup("FPGA"); + m_interfaceType = m_config->readEntry("interface", "serial"); + if (m_interfaceType == "serial") { + if (setupSerial() != 0) { + if (enableDebug) { + printf("[DEBUG] Connection from %s closed due to serial port initialization failure\n\r", m_remoteHost.ascii()); fflush(stdout); + } + close(); + return; + } + } + else if (m_interfaceType == "gpmc") { + if (setupGPMC() != 0) { + if (enableDebug) { + printf("[DEBUG] Connection from %s closed due to GPMC initialization failure\n\r", m_remoteHost.ascii()); fflush(stdout); + } + close(); + return; + } + } + else { if (enableDebug) { - printf("[DEBUG] Connection from %s closed due to serial port initialization failure\n\r", m_remoteHost.ascii()); fflush(stdout); + printf("[DEBUG] Connection from %s closed due to incorrect interface type specification in configuration file\n\r", m_remoteHost.ascii()); fflush(stdout); } close(); return; @@ -212,6 +235,40 @@ int FPGASocket::setupSerial() { return 0; } +int FPGASocket::setupGPMC() { + int i; + int ret; + + m_stateTXRequested = false; + m_stateImageRXRequested = false; + m_stateImageTXRequested = false; + + ret = setup_gpmc_bbb(); + if (ret == 0) { + // Verify attached uLab hardware model and version + unsigned char model = read_gpmc(0x00); + unsigned char version = read_gpmc(0x01); + if ((model != 0x42) || (version < 1)) { + printf("A compatible uLab hardware debug interface was not detected! Please verify your configuration.\n"); + return -1; + } + printf("[DEBUG] Detected a compatible uLab hardware debug interface (model number 0x%02x, firmware version 0x%02x)\n", model, version); + + // Clear out DSP and LCD RAM + unsigned char dsp_ram_bits = read_gpmc(0x0b); + unsigned int dsp_ram_offset = (1 << dsp_ram_bits); + unsigned int dsp_ram_size = (1 << dsp_ram_bits); + for (i=0; i 0) { - writeBlock(buffer, cc); - flush(); - transferred_data = true; - if (enableDebug) { - printf("[DEBUG] Got %d bytes from the serial port\n\r", cc); fflush(stdout); - } - } - if (canReadData()) { - cc = readBlock(buffer, 1024); + if (m_interfaceType == "serial") { + cc = read(m_fd_tty, buffer, 1024); if (cc > 0) { - ret = write(m_fd_tty, buffer, cc); - - // HACK - // This works around a buffer overflow on FTDI serial devices - // It may not be sufficient for baudrates less than 115200! - if (cc > 128) { - usleep(100000); + writeBlock(buffer, cc); + flush(); + transferred_data = true; + if (enableDebug) { + printf("[DEBUG] Got %d bytes from the serial port\n\r", cc); fflush(stdout); } - - while ((ret < 0) && (errno == EAGAIN)) { - usleep(1000); + } + if (canReadData()) { + cc = readBlock(buffer, 1024); + if (cc > 0) { ret = write(m_fd_tty, buffer, cc); + + // HACK + // This works around a buffer overflow on FTDI serial devices + // It may not be sufficient for baudrates less than 115200! + if (cc > 128) { + usleep(100000); + } + + while ((ret < 0) && (errno == EAGAIN)) { + usleep(1000); + ret = write(m_fd_tty, buffer, cc); + } + if (ret < 0) { + // ERROR + printf("[ERROR] Failed to transmit data to serial port (%s, code %d)! Continuing, but data was likely lost\n\r", strerror(errno), errno); fflush(stdout); + } + ioctl(m_fd_tty, TCFLSH, FLUSH_OUT); + transferred_data = true; + if (enableDebug) { + printf("[DEBUG] Got %d bytes from the network interface\n\r", cc); fflush(stdout); + } } - if (ret < 0) { - // ERROR - printf("[ERROR] Failed to transmit data to serial port (%s, code %d)! Continuing, but data was likely lost\n\r", strerror(errno), errno); fflush(stdout); + } + } + else if (m_interfaceType == "gpmc") { + if (m_stateImageTXRequested) { + if (read_gpmc(0x0a) & 0x02) { + m_stateImageTXRequested = false; + + // Transmit image back to client + unsigned char dsp_ram_bits = read_gpmc(0x0b); + unsigned int dsp_ram_size = (1 << dsp_ram_bits); + unsigned int dsp_ram_offset = (1 << dsp_ram_bits); + TQByteArray dataToSend(dsp_ram_size); + memcpy_from_gpmc(dataToSend.data(), dsp_ram_offset, dsp_ram_size); + int offset = 0; + while (offset <= dsp_ram_size) { + writeBlock(dataToSend.data()+offset, 1024); + writeBufferedData(); + offset = offset + 1024; + } } - ioctl(m_fd_tty, TCFLSH, FLUSH_OUT); - transferred_data = true; - if (enableDebug) { - printf("[DEBUG] Got %d bytes from the network interface\n\r", cc); fflush(stdout); + } + else if (m_stateTXRequested) { + m_stateTXRequested = false; + + char data[42]; + + // Read state data from memory map and assemble a reply + memcpy_from_gpmc(data+0, 0x20, 0x1f); // LCD display + data[32] = 1; // Input mode (locked to Remote) + data[33] = read_gpmc(0x0b); // Number of address bits of DSP RAM + data[34] = read_gpmc(0x02); // 4-bit LEDs + data[35] = read_gpmc(0x03); // 8-bit LEDs + data[36] = read_gpmc(0x04); // 16-bit LEDs (upper byte) + data[37] = read_gpmc(0x05); // 16-bit LEDs (lower byte) + memcpy_from_gpmc(data+38, 0x06, 0x04); // 7-segment LED display + + writeBlock(data, 42); + writeBufferedData(); + } + if (canReadData()) { + int read_offset = 0; + cc = readBlock(buffer, 1024); + if (cc > 0) { + if (m_stateImageRXRequested) { + unsigned char dsp_ram_bits = read_gpmc(0x0b); + unsigned int dsp_ram_offset = (1 << dsp_ram_bits); + unsigned int dsp_ram_size = (1 << dsp_ram_bits); + + memcpy_to_gpmc(buffer, (dsp_ram_offset + m_stateImageRXCounter), cc); + + m_stateImageRXCounter = m_stateImageRXCounter + cc; + if (m_stateImageRXCounter >= dsp_ram_size) { + m_stateImageRXRequested = false; + m_stateImageTXRequested = true; + } + } + else { + // Parse and write state data to the memory map + while (read_offset < cc) { + if (buffer[read_offset+0] == 'M') { + // Receive image data and store in FPGA memory + m_stateImageRXRequested = true; + m_stateImageTXRequested = false; + m_stateImageRXCounter = 0; + read_offset = read_offset + 2; + } + else if (buffer[read_offset+0] == 'L') { + m_stateTXRequested = true; + read_offset = read_offset + 2; + } + else if (buffer[read_offset+0] == 'I') { + write_gpmc(0x02, buffer[read_offset+2]); + read_offset = read_offset + 4; + } + else if (buffer[read_offset+0] == 'B') { + write_gpmc(0x03, buffer[read_offset+2]); + read_offset = read_offset + 4; + } + else if (buffer[read_offset+0] == 'C') { + write_gpmc(0x04, buffer[read_offset+2]); + write_gpmc(0x05, buffer[read_offset+4]); + read_offset = read_offset + 6; + } + } + if (m_stateImageTXRequested) { + m_stateImageTXRequested = false; + } + } + + transferred_data = true; + if (enableDebug) { + printf("[DEBUG] Got %d bytes from the network interface\n\r", cc); fflush(stdout); + } } } } diff --git a/servers/fpga_server_lin/src/fpga_conn.h b/servers/fpga_server_lin/src/fpga_conn.h index 94ac3e3..0310101 100644 --- a/servers/fpga_server_lin/src/fpga_conn.h +++ b/servers/fpga_server_lin/src/fpga_conn.h @@ -55,6 +55,7 @@ class FPGASocket : public TDEKerberosServerSocket void finishKerberosHandshake(); void connectionClosedHandler(); int setupSerial(); + int setupGPMC(); void commandLoop(); private: @@ -63,6 +64,11 @@ class FPGASocket : public TDEKerberosServerSocket int m_pollInterval; bool enableDebug; TQString m_remoteHost; + TQString m_interfaceType; + bool m_stateTXRequested; + bool m_stateImageRXRequested; + int m_stateImageRXCounter; + bool m_stateImageTXRequested; int m_fd_tty; TQTimer* m_kerberosInitTimer;