// Concept tester for interfacing from the raspberry ppi // with theGigatron TTL microcomputer using a microcontroller // hooked to the input port (J4). // (adapted from ../Utils/LoaderTest/LoaderTest.ino) // /!\ it is not very reliable. /!\ // The problem is that the raspberry pi is not a realtime system, whereas precise timing is needed to // communicate with the gigatron. // So: // - on the one hand, the program tries to hint the kernel that it really // needs highpriority as-real-time-as-possible scheduling during certain periods // (and try to yield control back to the system when it needs to wait, hopping that the // system tasks will be scheduled at those times) // - on the other hand it measures the time spent during a payload frame, and try to detect if // it has been interrupted (sequence took longer than expected), and thus that the frame // is probably corrupted on the receiving end. // - if it thinks the frame is corrupt, reset the checksum and tries to send it again. #include #include #include #include #include #include #include #include typedef unsigned char byte; typedef int bool; // === GPIO acess ============ // =========================== //pins: rpi2 BCM / rpi2 board / Gigatron DB9 (J4) static int GROUND = 0; // 34 8 static int SER_DATA = 12; // 32 2 blanc static int SER_LATCH = 16; // 36 3 vert static int SER_PULSE = 20; // 38 4 rouge // ------------------------------------------ #define BCM2708_PERI_BASE 0x20000000 //rpi1 #define BCM2709_PERI_BASE 0x3F000000 //rpi2 #define GPIO_BASE (BCM2709_PERI_BASE + 0x200000) /* GPIO controller */ #define FSEL_OFFSET 0 // 0x0000 #define SET_OFFSET 7 // 0x001c / 4 #define CLR_OFFSET 10 // 0x0028 / 4 #define PINLEVEL_OFFSET 13 // 0x0034 / 4 volatile unsigned *gpio; void gpio_setup() { int mem_fd; if ((mem_fd = open("/dev/gpiomem", O_RDWR|O_SYNC) ) < 0) { printf("can't open /dev/gpiomem \n"); exit(-1); } size_t BLOCK_SIZE = (4*1024); static void *gpio_map; gpio_map = mmap(NULL, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, mem_fd, 0); close(mem_fd); //No need to keep mem_fd open after mmap if (gpio_map == MAP_FAILED) { printf("mmap error %d %m\n", (int)gpio_map); exit(-1); } gpio = (volatile unsigned *)gpio_map; // try minimizing interruption causes... mlockall(MCL_CURRENT); // (probably useless very little mem is used) } void gpio_setrealtimesched(int enable) { if (enable) { struct sched_param sp = {32}; int err = sched_setscheduler(0, SCHED_FIFO, &sp); if (err!=0) { printf("could not switch to realtime prio: %m\n"); } // nb for pthreads: pthread_setschedparam(pthread_self(), SCHED_FIFO, &sp)) } else { struct sched_param sp = {0}; int err = sched_setscheduler(0, SCHED_OTHER, &sp); if (err!=0) { printf("could not switch back to normal prio: %m\n"); } } } void gpio_setmode_out(int pin) { int offset = FSEL_OFFSET + (pin/10); int shift = (pin%10)*3; *(gpio+offset) = (*(gpio+offset) & ~(7<> 1; // Send bit gpio_write(SER_DATA, bit); // Wait for bit transfer at horizontal sync POSITIVE edge. // This timing is tight for the first bit of the first byte and while (gpio_read(SER_PULSE)) {Nop();} // Ensure hSync is LOW first while (!gpio_read(SER_PULSE)) {Nop();} // Then wait for hSync to rise } } void SendController(int button, int frames) { gpio_setrealtimesched(1); // Note: The kit's controller gives inverted signals. for (int i=0; i> 1; // Send bit gpio_write(SER_DATA, bit); // Wait for bit transfer at horizontal sync POSITIVE edge. // This timing is tight for the first bit of the first byte and while (gpio_read(SER_PULSE)) { Nop(); } // Ensure hSync is LOW first while (!gpio_read(SER_PULSE)) { Nop(); } // Then wait for hSync to rise } checksum += val; } bool SendFrame(byte ProtocolByte, int len, unsigned address, const byte* message) { // Send one frame of data // // A frame has 65*8-2=518 bits, including protocol byte and checksum. // The reasons for the two "missing" bits are: // 1. From start of vertical pulse, there are 35 scanlines remaining // in vertical blank. But we also need the payload bytes to align // with scanlines where the interpreter runs, so the Gigatron doesn't // have to shift everything it receives by 1 bit. // 2. There is a 1 bit latency inside the 74HCT595 for the data bit, // but (obviously) not for the sync signals. // All together, we drop 2 bits from the 2nd byte in a frame. This achieves // byte alignment for the Gigatron at visible scanline 3, 11, 19, ... etc. // Wait vertical sync NEGATIVE edge to sync with loader while (!gpio_read(SER_LATCH)) {Nop();} // Ensure vSync is HIGH first while (gpio_read(SER_LATCH)) {Nop();} // Then wait for vSync to drop unsigned begin = Clock(); SendBits(ProtocolByte, 8); checksum += ProtocolByte << 6; // Keep Loader.gcl dumb SendBits((byte)len, 6); // Length 0, 1..60 SendBits((byte)(address&255), 8); // Low address bits SendBits((byte)(address>>8), 8); // High address bits for (byte i=0; i 0) { byte n = len < FramePayload ? len : FramePayload; bool valid = SendFrame('L', n, address, data); if (valid) { address += n; data += n; len -= n; } else { // retry... (and reset checksum) ResetChecksum(); while (gpio_read(SER_LATCH)) { Nop(); } // skip a frame for the checksum to reset on the other side Delay(10); // let the raspi run during the frame. } } // Wait for vBlank to start so we're 100% sure to skip one frame and // the checksum resets on the other side. (This is a bit pedantic) while (gpio_read(SER_LATCH)) { Nop(); } } void SendGt1File(const byte* gt1) { const byte* ptr = gt1; int firstsegment = 1; gpio_setrealtimesched(1); for (;;) { unsigned addrhi = *ptr++; if (addrhi == 0 && !firstsegment) break; // done. unsigned addrlo = *ptr++; unsigned address = (addrhi<<8) + addrlo; int len = *ptr++; if (len == 0) len = 256; // Check that segment doesn't cross the page boundary if ((address & 255) + len > 256) { printf("GT1 data error (page overflow)\n"); return; } //printf("loading %d bytes at @%X\n", len, address); SendGt1Segment(address, len, ptr); ptr += len; firstsegment = 0; Delay(10); // we have a full frame to wait (16ms), so hopefully os activity can take place now instead of interrupting us during time sensitive code. }; unsigned addrhi = *ptr++; unsigned addrlo = *ptr++; unsigned startaddress = (addrhi<<8) + addrlo; if (startaddress != 0) { printf("executing from @%X\n", startaddress); SendGt1Execute(startaddress); } gpio_setrealtimesched(0); } int main(int argc, char **argv) { if (argc < 2) { printf("usage: %s [skip]\n", argv[0]); return -1; } int fd = open(argv[1], O_RDONLY); if (fd<0) { printf("could not open '%s'\n", argv[1]); return -1; } byte gt1data[32768]; int len = (int)read(fd, gt1data, 32768); if (len == 0) { printf("could not read '%s'\n", argv[1]); return -1; } else { printf("loaded %d bytes\n", len); } close(fd); int skipmenu = (argc >= 3 && strcmp(argv[2], "skip") == 0) ; // Set up gpi pointer for direct register access gpio_setup(); printf("Establishing communication with gigatron....\n"); // Set GPIO pins mode for gigatron communication gpio_setmode_in(SER_LATCH); gpio_setmode_in(SER_PULSE); gpio_setmode_out(SER_DATA); gpio_write(SER_DATA, 1); // Send 1 when idle printf("Detecting device: %s\n", (DetectGigatron()?"ok":"failed")); Delay(500); if (!skipmenu) { printf("Reset\n"); SendReset(); printf("start loader\n"); StartLoader(); } SendGt1File(gt1data); return 0; }