summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTimothy Pearson <kb9vqf@pearsoncomputing.net>2014-01-10 02:58:09 -0600
committerTimothy Pearson <kb9vqf@pearsoncomputing.net>2014-01-10 02:58:09 -0600
commit4436bddc8ceecb5277de73de6c929a3bb6722143 (patch)
tree1fc6dde36e5339cd3d1e2f669facc01c9bdaede5
parent4123289a7a118ab922a815e3c216e3d6fe306d88 (diff)
downloadulab-4436bddc8ceecb5277de73de6c929a3bb6722143.tar.gz
ulab-4436bddc8ceecb5277de73de6c929a3bb6722143.zip
Add GPMC interface to FPGA server
-rw-r--r--servers/fpga_server_lin/src/Makefile.am2
-rw-r--r--servers/fpga_server_lin/src/bbb-gpmc-init.cpp181
-rw-r--r--servers/fpga_server_lin/src/bbb-gpmc-init.h28
-rw-r--r--servers/fpga_server_lin/src/fpga_conn.cpp213
-rw-r--r--servers/fpga_server_lin/src/fpga_conn.h6
5 files changed, 399 insertions, 31 deletions
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 <sys/time.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+
+#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<length; i++) {
+ *destination = *(gpio_char + register_offset);
+ destination++;
+ register_offset++;
+ }
+}
+
+void memcpy_to_gpmc(char* source, unsigned int register_offset, unsigned int length) {
+ unsigned int i;
+ for (i=0; i<length; i++) {
+ *(gpio_char + register_offset) = *source;
+ source++;
+ register_offset++;
+ }
+}
+
+/** END: Low-Level I/O Implementation **/ \ No newline at end of file
diff --git a/servers/fpga_server_lin/src/bbb-gpmc-init.h b/servers/fpga_server_lin/src/bbb-gpmc-init.h
new file mode 100644
index 0000000..6175ed1
--- /dev/null
+++ b/servers/fpga_server_lin/src/bbb-gpmc-init.h
@@ -0,0 +1,28 @@
+/*
+ * 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
+ */
+
+int setup_gpmc_bbb();
+
+void write_gpmc(unsigned int register_offset, unsigned char data);
+unsigned char read_gpmc(unsigned int register_offset);
+void memcpy_from_gpmc(char* destination, unsigned int register_offset, unsigned int length);
+void memcpy_to_gpmc(char* source, unsigned int register_offset, unsigned int length); \ No newline at end of file
diff --git a/servers/fpga_server_lin/src/fpga_conn.cpp b/servers/fpga_server_lin/src/fpga_conn.cpp
index fdd54fd..a20cad9 100644
--- a/servers/fpga_server_lin/src/fpga_conn.cpp
+++ b/servers/fpga_server_lin/src/fpga_conn.cpp
@@ -35,6 +35,7 @@
#include <sys/signal.h>
#include <sys/types.h>
#include <sys/ioctl.h>
+#include <math.h>
#include <tqtimer.h>
@@ -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<dsp_ram_size; i++) {
+ write_gpmc(dsp_ram_offset + i, 0x00);
+ }
+ for (i=0; i<32; i++) {
+ write_gpmc(0x20 + i, 0x00);
+ }
+ }
+
+ return 0;
+}
+
void FPGASocket::commandLoop() {
int cc;
int ret;
@@ -222,39 +279,135 @@ void FPGASocket::commandLoop() {
try {
transferred_data = false;
if (state() == TQSocket::Connected) {
- cc = read(m_fd_tty, buffer, 1024);
- if (cc > 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;