gateware

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

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:
MMakefile | 6+++++-
Ahdl/crc8_serial.sv | 37+++++++++++++++++++++++++++++++++++++
Ahdl/uart_debug_ifc.sv | 166+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ahdl/uart_rx.sv | 97+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/crc8.h | 100+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/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; +}