commit 87b8699ce5c9a8b39120021a1e9df593c0c8881b
parent 398d5a6472fcd41c5ff2823bdb0bc6a84348bae4
Author: Brian Swetland <swetland@frotz.net>
Date: Sat, 15 Dec 2018 05:50:23 -0800
udebug: uart debug interface, hdl and commandline tool
Handle simple packets of <SOF> <CMD> <DAT0> <DAT1> <DAT2> <DAT3> <CRC8>
SOF is 0xCD
CMD 0x01 sets the address register to DAT0:3 (little endian)
CMD 0x00 writes DAT0:3 (little endian) to address and increments address
CMD 0x02 clears error flag
All other commands or invalid packets set the error flag.
Diffstat:
M | Makefile | | | 6 | +++++- |
A | hdl/crc8_serial.sv | | | 37 | +++++++++++++++++++++++++++++++++++++ |
A | hdl/uart_debug_ifc.sv | | | 166 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | hdl/uart_rx.sv | | | 97 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | src/crc8.h | | | 100 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | src/udebug.c | | | 219 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
6 files changed, 624 insertions(+), 1 deletion(-)
diff --git a/Makefile b/Makefile
@@ -38,11 +38,15 @@ out/d16: src/d16v5.c
@mkdir -p out
gcc -g -Wall -O1 -o out/d16 -DSTANDALONE=1 src/d16v5.c
+out/udebug: src/udebug.c
+ @mkdir -p out
+ gcc -g -Wall -Wno-unused-result -O1 -o out/udebug src/udebug.c
+
out/icetool: src/icetool.c src/ftdi.c src/ftdi.h
@mkdir -p out
gcc -g -Wall -O1 -o out/icetool src/icetool.c src/ftdi.c -lusb-1.0 -lrt
-tools:: out/a16 out/d16 out/icetool
+tools:: out/a16 out/d16 out/icetool out/udebug
build-all-buildable:: $(ALL_BUILDS) tools
diff --git a/hdl/crc8_serial.sv b/hdl/crc8_serial.sv
@@ -0,0 +1,37 @@
+// Copyright 2018, Brian Swetland <swetland@frotz.net>
+// Licensed under the Apache License, Version 2.0.
+
+// 0x9C 10011100x // koopman notation (low bit implied)
+// 0x39 x00111001 // truncated notation (high bit implied)
+
+module crc8_serial(
+ input clk,
+ input din,
+ input en,
+ input rst,
+ output [7:0]crc
+ );
+
+reg [7:0]r;
+
+wire d = din ^ r[7];
+
+always @(posedge clk) begin
+ if (rst) begin
+ r <= 8'hFF;
+ end else if (en) begin
+ r[0] <= d;
+ r[1] <= r[0];
+ r[2] <= r[1];
+ r[3] <= r[2] ^ d;
+ r[4] <= r[3] ^ d;
+ r[5] <= r[4] ^ d;
+ r[6] <= r[5];
+ r[7] <= r[6];
+ end
+end
+
+assign crc = { r[0],r[1],r[2],r[3],r[4],r[5],r[6],r[7] };
+
+endmodule
+
diff --git a/hdl/uart_debug_ifc.sv b/hdl/uart_debug_ifc.sv
@@ -0,0 +1,166 @@
+// Copyright 2018, Brian Swetland <swetland@frotz.net>
+// Licensed under the Apache License, Version 2.0.
+
+module uart_debug_ifc(
+ input sys_clk,
+ output sys_wr,
+ output [15:0]sys_waddr,
+ output [15:0]sys_wdata,
+ input uart_rx,
+ output uart_tx,
+ output led_red,
+ output led_grn
+);
+
+reg crc_rst = 1'b0;
+reg crc_rst_next;
+
+wire crc_din;
+wire crc_en;
+wire [7:0]crc;
+
+wire [7:0]rxdata;
+wire rxready;
+
+uart_rx rx_uart(
+ .clk(sys_clk),
+ .rx(uart_rx),
+ .data(rxdata),
+ .ready(rxready),
+ .crc_din(crc_din),
+ .crc_en(crc_en)
+ );
+
+crc8_serial rx_crc(
+ .clk(sys_clk),
+ .din(crc_din),
+ .en(crc_en),
+ .rst(crc_rst),
+ .crc(crc)
+ );
+
+reg wr = 1'b0;
+reg wr_next;
+
+reg [31:0]addr = 32'd0;
+reg [31:0]data = 32'd0;
+reg [7:0]cmd = 8'd0;
+
+reg [7:0]cmd_next;
+reg [7:0]dat0_next;
+reg [7:0]dat1_next;
+reg [7:0]dat2_next;
+reg [7:0]dat3_next;
+reg [31:0]addr_next;
+
+localparam SINIT = 3'd0;
+localparam SCMD = 3'd1;
+localparam SDAT0 = 3'd2;
+localparam SDAT1 = 3'd3;
+localparam SDAT2 = 3'd4;
+localparam SDAT3 = 3'd5;
+localparam SCRC = 3'd6;
+localparam SIDLE = 3'd7;
+
+reg [2:0]state = SINIT;
+reg [2:0]state_next;
+
+reg error = 1'b0;
+reg error_next;
+
+always_comb begin
+ state_next = state;
+ wr_next = 1'b0;
+ crc_rst_next = 1'b0;
+ cmd_next = cmd;
+ dat0_next = data[7:0];
+ dat1_next = data[15:8];
+ dat2_next = data[23:16];
+ dat3_next = data[31:24];
+ addr_next = addr;
+ error_next = error;
+
+ case (state)
+ SINIT: begin
+ state_next = SIDLE;
+ crc_rst_next = 1'b1;
+ end
+ SIDLE: begin
+ if (rxready) begin
+ if (rxdata == 8'hCD) begin
+ state_next = SCMD;
+ end else begin
+ error_next = 1'b1;
+ crc_rst_next = 1'b1;
+ end
+ end
+ end
+ SCMD: begin
+ if (rxready) begin
+ state_next = SDAT0;
+ cmd_next = rxdata;
+ end
+ end
+ SDAT0: begin
+ if (rxready) begin
+ state_next = SDAT1;
+ dat0_next = rxdata;
+ end
+ end
+ SDAT1: begin
+ if (rxready) begin
+ state_next = SDAT2;
+ dat1_next = rxdata;
+ end
+ end
+ SDAT2: begin
+ if (rxready) begin
+ state_next = SDAT3;
+ dat2_next = rxdata;
+ end
+ end
+ SDAT3: begin
+ if (rxready) begin
+ state_next = SCRC;
+ dat3_next = rxdata;
+ end
+ end
+ SCRC: begin
+ if (rxready) begin
+ state_next = SIDLE;
+ crc_rst_next = 1'b1;
+ if (crc == 8'd0) begin
+ case (cmd)
+ 8'h00: wr_next = 1'b1;
+ 8'h01: addr_next = data;
+ 8'h02: error_next = 1'b0;
+ default: error_next = 1'b1;
+ endcase
+ end else begin
+ error_next = 1'b1;
+ end
+ end
+ end
+ endcase
+end
+
+always_ff @(posedge sys_clk) begin
+ state <= state_next;
+ cmd <= cmd_next;
+ addr <= wr ? (addr + 32'd1) : addr_next;
+ data <= { dat3_next, dat2_next, dat1_next, dat0_next };
+ crc_rst <= crc_rst_next;
+ wr <= wr_next;
+ error <= error_next;
+end
+
+assign sys_wr = wr;
+assign sys_waddr = addr[15:0];
+assign sys_wdata = data[15:0];
+
+assign led_grn = ~(crc == 8'd0);
+assign led_red = ~error;
+
+assign uart_tx = uart_rx;
+
+endmodule
diff --git a/hdl/uart_rx.sv b/hdl/uart_rx.sv
@@ -0,0 +1,97 @@
+// Copyright 2018, Brian Swetland <swetland@frotz.net>
+// Licensed under the Apache License, Version 2.0.
+
+// Assumes clk is 12M and baudrate is 1M
+// TODO: parameterize this a bit
+
+module uart_rx(
+ input clk,
+ input rx,
+ output [7:0]data,
+ output ready,
+ output crc_din,
+ output crc_en
+);
+
+// active indicates reception in progress
+reg active = 1'b0;
+reg active_next;
+
+// bitcount is a downcounter of bits remaining to receive
+reg [2:0]bitcount = 3'd0;
+reg [2:0]bitcount_next;
+
+wire bitcount_done;
+wire [2:0]bitcount_minus_one;
+assign { bitcount_done, bitcount_minus_one } = { 1'b0, bitcount } - 4'd1;
+
+// tickcount is a downcounter of sys_clk ticks until next bit
+reg [3:0]tickcount = 4'd0;
+reg [3:0]tickcount_next;
+
+wire tick;
+wire [3:0]tickcount_minus_one;
+assign { tick, tickcount_minus_one } = { 1'b0, tickcount } - 5'd1;
+
+// receive shift register
+reg [7:0]rxdata;
+reg [7:0]rxdata_next;
+
+// most recent 3 bits for edge detection
+reg [2:0]rxedge;
+
+// drives the ready flag
+reg signal = 1'b0;
+reg signal_next;
+
+assign data = rxdata;
+assign ready = signal;
+
+// pass inbound bits to serial crc engine
+assign crc_din = rxedge[2];
+assign crc_en = active & tick;
+
+always_comb begin
+ signal_next = 1'b0;
+ active_next = active;
+ bitcount_next = bitcount;
+ tickcount_next = tickcount;
+ rxdata_next = rxdata;
+
+ if (active) begin
+ if (tick) begin
+ rxdata_next = { rxedge[2], rxdata[7:1] };
+ if (bitcount_done) begin
+ active_next = 1'b0;
+ signal_next = 1'b1;
+ end else begin
+ bitcount_next = bitcount_minus_one;
+ // 12 (-1) ticks to the next bit center
+ tickcount_next = 4'd11;
+ end
+ end else begin
+ tickcount_next = tickcount_minus_one;
+ end
+ end else begin
+ if (rxedge == 3'b001) begin
+ // 12 ticks center to center + 4 to adjust from
+ // 2 ticks into the start bit = 16 (-1):
+ tickcount_next = 4'd15;
+ // 8 (-1) bits to receive:
+ bitcount_next = 3'd7;
+ // start!
+ active_next = 1'b1;
+ end
+ end
+end
+
+always_ff @(posedge clk) begin
+ rxedge <= { rx, active ? 2'b0 : rxedge[2:1] };
+ rxdata <= rxdata_next;
+ signal <= signal_next;
+ active <= active_next;
+ bitcount <= bitcount_next;
+ tickcount <= tickcount_next;
+end
+
+endmodule
diff --git a/src/crc8.h b/src/crc8.h
@@ -0,0 +1,100 @@
+// Copyright 2018, Brian Swetland <swetland@frotz.net>
+// Licensed under the Apache License, Version 2.0.
+
+#pragma once
+
+#include <stdint.h>
+
+static uint8_t crc8tab[256] = {
+ "\x00\x72\xe4\x96\xf1\x83\x15\x67\xdb\xa9\x3f\x4d\x2a\x58\xce\xbc"
+ "\x8f\xfd\x6b\x19\x7e\x0c\x9a\xe8\x54\x26\xb0\xc2\xa5\xd7\x41\x33"
+ "\x27\x55\xc3\xb1\xd6\xa4\x32\x40\xfc\x8e\x18\x6a\x0d\x7f\xe9\x9b"
+ "\xa8\xda\x4c\x3e\x59\x2b\xbd\xcf\x73\x01\x97\xe5\x82\xf0\x66\x14"
+ "\x4e\x3c\xaa\xd8\xbf\xcd\x5b\x29\x95\xe7\x71\x03\x64\x16\x80\xf2"
+ "\xc1\xb3\x25\x57\x30\x42\xd4\xa6\x1a\x68\xfe\x8c\xeb\x99\x0f\x7d"
+ "\x69\x1b\x8d\xff\x98\xea\x7c\x0e\xb2\xc0\x56\x24\x43\x31\xa7\xd5"
+ "\xe6\x94\x02\x70\x17\x65\xf3\x81\x3d\x4f\xd9\xab\xcc\xbe\x28\x5a"
+ "\x9c\xee\x78\x0a\x6d\x1f\x89\xfb\x47\x35\xa3\xd1\xb6\xc4\x52\x20"
+ "\x13\x61\xf7\x85\xe2\x90\x06\x74\xc8\xba\x2c\x5e\x39\x4b\xdd\xaf"
+ "\xbb\xc9\x5f\x2d\x4a\x38\xae\xdc\x60\x12\x84\xf6\x91\xe3\x75\x07"
+ "\x34\x46\xd0\xa2\xc5\xb7\x21\x53\xef\x9d\x0b\x79\x1e\x6c\xfa\x88"
+ "\xd2\xa0\x36\x44\x23\x51\xc7\xb5\x09\x7b\xed\x9f\xf8\x8a\x1c\x6e"
+ "\x5d\x2f\xb9\xcb\xac\xde\x48\x3a\x86\xf4\x62\x10\x77\x05\x93\xe1"
+ "\xf5\x87\x11\x63\x04\x76\xe0\x92\x2e\x5c\xca\xb8\xdf\xad\x3b\x49"
+ "\x7a\x08\x9e\xec\x8b\xf9\x6f\x1d\xa1\xd3\x45\x37\x50\x22\xb4\xc6"
+};
+
+unsigned crc8(unsigned crc, void* ptr, size_t len) {
+ uint8_t* p = ptr;
+ while (len-- > 0) {
+ crc = crc8tab[crc ^ (*p++)];
+ }
+ return crc;
+}
+
+#ifdef CRC8_MAKE_TABLE
+// 0x9C 10011100x // koopman notation (low bit implied)
+// 0x39 x00111001 // truncated notation (high bit implied)
+//
+// LSB
+#define POLY 0x39
+
+void make_crc8tab(void) {
+ unsigned c, d, i;
+ for (d = 0; d < 256; d++) {
+ c = d;
+ for (i = 0; i < 8; i++) {
+ if (c & 0x80) {
+ c = (POLY) ^ (c << 1);
+ } else {
+ c <<= 1;
+ }
+ }
+ //crc8tab[d] = c; //MSB
+ crc8tab[rev(d)] = rev(c); //LSB
+ }
+
+ printf("static uint8_t crc8tab[256] = {");
+ for (i = 0; i < 256; i++) {
+ if ((i % 16) == 0) {
+ printf(i ? "\"\n \"" : "\n \"");
+ }
+ printf("\\x%02x", crc8tab[i]);
+ }
+ printf("\"\n};\n");
+}
+#endif
+
+#ifdef CRC8_BIT_SERIAL_IMPL
+#define POLY 0x39
+
+static unsigned rev(unsigned n) {
+ unsigned m = 0;
+ for (unsigned i = 0; i < 8; i++) {
+ m = (m << 1) | (n & 1);
+ n >>= 1;
+ }
+ return m;
+}
+
+unsigned crc8_serial(unsigned crc, unsigned data) {
+ for (unsigned n = 0; n < 8; n++) {
+ unsigned din = data & 1;
+ data >>= 1;
+ unsigned crchi = (crc & 0x80) ? 1 : 0;
+ din ^= crchi;
+ crc <<= 1;
+ if (din) crc ^= POLY;
+ }
+ return crc & 0xFF;
+}
+
+unsigned crc8(void* ptr, size_t len) {
+ uint8_t* p = ptr;
+ unsigned crc = 0xff;
+ while (len-- > 0) {
+ crc = crc8_serial(crc, *p++);
+ }
+ return rev(crc);
+}
+#endif
diff --git a/src/udebug.c b/src/udebug.c
@@ -0,0 +1,219 @@
+// Copyright 2018, Brian Swetland <swetland@frotz.net>
+// Licensed under the Apache License, Version 2.0.
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include <sys/fcntl.h>
+
+#include "crc8.h"
+
+int openserial(const char *device, unsigned speed) {
+ struct termios tio;
+ int fd;
+
+ /* open the serial port non-blocking to avoid waiting for cd */
+ if ((fd = open(device, O_RDWR | O_NOCTTY | O_NDELAY)) < 0) {
+ return -1;
+ }
+
+ memset(&tio, 0, sizeof(tio));
+ tio.c_iflag |= IGNBRK | IGNPAR; // ignore break, ignore parity/frame err
+ tio.c_cflag |= CS8 | CREAD | CLOCAL; // 8bit, enable rx, no modem lines
+ tio.c_cc[VMIN] = 1; // min chars to read
+ tio.c_cc[VTIME] = 0; // blocking read
+ cfmakeraw(&tio);
+ cfsetispeed(&tio, speed);
+ cfsetospeed(&tio, speed);
+
+ if (tcsetattr(fd, TCSAFLUSH, &tio)) {
+ fprintf(stderr, "error: cannot set serial port attributes: %d %s\n", errno, strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+void cmd(int fd, unsigned cmd, unsigned data) {
+ uint8_t msg[7];
+ msg[0] = 0xCD;
+ msg[1] = cmd;
+ msg[2] = data;
+ msg[3] = data >> 8;
+ msg[4] = data >> 16;
+ msg[5] = data >> 24;
+ msg[6] = crc8(0xFF, msg, 6);
+ write(fd, msg, 7);
+}
+
+void cmd_wr_u8(int fd, uint32_t addr, uint8_t* p, size_t len) {
+ if (len > 4096) {
+ return;
+ }
+ uint8_t data[4097*7];
+ uint8_t* msg = data;
+
+ msg[0] = 0xCD;
+ msg[1] = 0x01;
+ msg[2] = addr;
+ msg[3] = addr >> 8;
+ msg[4] = addr >> 16;
+ msg[5] = addr >> 24;
+ msg[6] = crc8(0xFF, msg, 6);
+ msg += 7;
+
+ while (len-- > 0) {
+ msg[0] = 0xCD;
+ msg[1] = 0x00;
+ msg[2] = *p++;
+ msg[3] = 0x00;
+ msg[4] = 0x00;
+ msg[5] = 0x00;
+ msg[6] = crc8(0xFF, msg, 6);
+ msg += 7;
+ }
+ write(fd, data, msg - data);
+}
+
+void cmd_wr_u16(int fd, uint32_t addr, uint16_t* p, size_t len) {
+ if (len > 4096) {
+ return;
+ }
+ uint8_t data[4097*7];
+ uint8_t* msg = data;
+
+ msg[0] = 0xCD;
+ msg[1] = 0x01;
+ msg[2] = addr;
+ msg[3] = addr >> 8;
+ msg[4] = addr >> 16;
+ msg[5] = addr >> 24;
+ msg[6] = crc8(0xFF, msg, 6);
+ msg += 7;
+
+ while (len-- > 0) {
+ msg[0] = 0xCD;
+ msg[1] = 0x00;
+ msg[2] = *p;
+ msg[3] = *p >> 8;
+ msg[4] = 0x00;
+ msg[5] = 0x00;
+ msg[6] = crc8(0xFF, msg, 6);
+ msg += 7;
+ p++;
+ }
+ write(fd, data, msg - data);
+}
+
+int usage(void) {
+ fprintf(stderr,
+ "usage: udebug <port> <command>*\n"
+ "\n"
+ "command: -load <addr> <hexfile> - write hex file\n"
+ " -write <addr> <value>... - write wolds\n"
+ " -reset - processor RST=1\n"
+ " -run - processor RST=0\n"
+ " -error - cause link error\n"
+ " -clear - clear link error\n"
+ );
+ return -1;
+}
+
+int main(int argc, char **argv) {
+ uint16_t addr;
+ uint16_t data[4096];
+ size_t n;
+
+ if (argc < 2) {
+ return usage();
+ }
+
+ int sfd = openserial(argv[1], B1000000);
+ if (sfd < 0) {
+ fprintf(stderr, "error: cannot open serial port '%s'\n", argv[1]);
+ return -1;
+ }
+
+ argc -= 2;
+ argv += 2;
+ while (argc > 0) {
+ if (!strcmp(argv[0], "-load")) {
+ if (argc < 3) {
+ return usage();
+ }
+ char buf[1024];
+ addr = strtoul(argv[1], 0, 16);
+ FILE *fp = fopen(argv[2], "r");
+ if (fp == NULL) {
+ fprintf(stderr, "error: cannot open '%s'\n", argv[3]);
+ return -1;
+ }
+ n = 0;
+ while (fgets(buf, 1024, fp)) {
+ if (!isalnum(buf[0])) continue;
+ if (n == 4096) return -1;
+ data[n++] = strtoul(buf, 0, 16);
+ }
+ fclose(fp);
+ cmd_wr_u16(sfd, addr, data, n);
+ argc -= 3;
+ argv += 3;
+ } else if (!strcmp(argv[0], "-write")) {
+ if (argc < 3) {
+ return usage();
+ }
+ addr = strtoul(argv[1], 0, 16);
+ argc -= 2;
+ argv += 2;
+ n = 0;
+ while (argc > 0) {
+ if (argv[0][0] == '-') {
+ break;
+ }
+ if (argv[0][0] == '/') {
+ char *s = argv[0] + 1;
+ while (*s != 0) {
+ if (n == 4096) return -1;
+ data[n++] = *s++;
+ }
+ } else {
+ if (n == 4096) return -1;
+ data[n++] = strtoul(argv[0], 0, 16);
+ }
+ argc--;
+ argv++;
+ }
+ cmd_wr_u16(sfd, addr, data, n);
+ } else if (!strcmp(argv[0], "-reset")) {
+ data[0] = 1;
+ cmd_wr_u16(sfd, 0xF000, data, 1);
+ argc--;
+ argv++;
+ } else if (!strcmp(argv[0], "-run")) {
+ data[0] = 0;
+ cmd_wr_u16(sfd, 0xF000, data, 1);
+ argc--;
+ argv++;
+ } else if (!strcmp(argv[0], "-error")) {
+ cmd(sfd, 0xFF, 0);
+ argc--;
+ argv++;
+ } else if (!strcmp(argv[0], "-clear")) {
+ cmd(sfd, 0x02, 0);
+ argc--;
+ argv++;
+ } else {
+ usage();
+ }
+ }
+
+ close(sfd);
+ return 0;
+}