From 38c5ae5b60eae9562b97da42f47af3861847f8e5 Mon Sep 17 00:00:00 2001 From: Julian Blake Kongslie Date: Sun, 8 May 2022 15:51:35 -0700 Subject: *Proper* serial port for memory downloads. 115200 8O2 RS232 with CRTRTS. --- Makefile | 35 +++++++--------- PLAN | 9 ++-- altera/clocks.sdc | 2 +- download.tcl | 34 --------------- hdl/core.sv | 2 - hdl/rs232.sv | 109 +++++++++++++++++++++++++++++++++--------------- hdl/top.sv | 122 ++++++++++++++++++++++++++---------------------------- tcl/init.tcl | 49 +++++++++++++++++++++- tool/con.c | 106 +++++++++++++++++++++++++++++++++++++++++++++++ tool/connect | 13 +++--- tool/download.tcl | 33 +++++++++++++++ tool/p8bin2uart.c | 2 +- 12 files changed, 353 insertions(+), 163 deletions(-) delete mode 100755 download.tcl create mode 100644 tool/con.c create mode 100755 tool/download.tcl diff --git a/Makefile b/Makefile index d25ca7c..0094f04 100644 --- a/Makefile +++ b/Makefile @@ -8,25 +8,22 @@ fpga: pdp8.sof .PHONY: fpga download-%: mem/% - p8bin2uart 1 $< | ./download.tcl 0 1 - p8bin2uart 1 $< | ./download.tcl 1 1 - p8bin2uart 1 $< | ./download.tcl 2 1 - p8bin2uart 1 $< | ./download.tcl 3 1 - p8bin2uart 1 $< | ./download.tcl 4 1 - p8bin2uart 1 $< | ./download.tcl 5 1 - p8bin2uart 1 $< | ./download.tcl 6 1 - p8bin2uart 1 $< | ./download.tcl 7 1 - p8bin2uart 1 $< | ./download.tcl 8 1 - p8bin2uart 1 $< | ./download.tcl 9 1 - p8bin2uart 1 $< | ./download.tcl 10 1 - p8bin2uart 1 $< | ./download.tcl 11 1 - p8bin2uart 1 $< | ./download.tcl 12 1 - p8bin2uart 1 $< | ./download.tcl 13 1 - p8bin2uart 1 $< | ./download.tcl 14 1 - p8bin2uart 1 $< | ./download.tcl 15 1 - -download-pal-%: build/%.bin - p8bin2uart 1 $< | ./download.tcl 0 1 + p8bin2uart 1 $< | tool/download.tcl 0 1 + p8bin2uart 1 $< | tool/download.tcl 1 1 +# p8bin2uart 1 $< | tool/download.tcl 2 1 +# p8bin2uart 1 $< | tool/download.tcl 3 1 +# p8bin2uart 1 $< | tool/download.tcl 4 1 +# p8bin2uart 1 $< | tool/download.tcl 5 1 +# p8bin2uart 1 $< | tool/download.tcl 6 1 +# p8bin2uart 1 $< | tool/download.tcl 7 1 +# p8bin2uart 1 $< | tool/download.tcl 8 1 +# p8bin2uart 1 $< | tool/download.tcl 9 1 +# p8bin2uart 1 $< | tool/download.tcl 10 1 +# p8bin2uart 1 $< | tool/download.tcl 11 1 +# p8bin2uart 1 $< | tool/download.tcl 12 1 +# p8bin2uart 1 $< | tool/download.tcl 13 1 +# p8bin2uart 1 $< | tool/download.tcl 14 1 +# p8bin2uart 1 $< | tool/download.tcl 15 1 term-mem: nios2-terminal --instance 0 diff --git a/PLAN b/PLAN index a2bfaaa..8b46103 100644 --- a/PLAN +++ b/PLAN @@ -1,7 +1,8 @@ -1. more scalable memory arbiter -2. debug rs232 uart -3. add cts/rts flow control to rs232 uart -4. rs232-based high speed memory downloader (nios terminal sucks) +0. increase speed of rs232 and make the rx side oversample +1. make rs232 come up in a cleaner state following reset +2. add cts/rts flow control to rs232 uart +3. rs232-based high speed memory downloader (nios terminal sucks) +4. more scalable memory arbiter 5. add pdp-8 instruction cache 6. add pdp-8 data cache 7. add global shared cache diff --git a/altera/clocks.sdc b/altera/clocks.sdc index fd99dad..c08f897 100644 --- a/altera/clocks.sdc +++ b/altera/clocks.sdc @@ -1,3 +1,3 @@ # This is the clock for timing analysis, not timing-driven synthesis. # See init.tcl for the other clock. -create_clock -period "30 MHz" clock +create_clock -period "50 MHz" clock diff --git a/download.tcl b/download.tcl deleted file mode 100755 index 635e54b..0000000 --- a/download.tcl +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env expect - -set timeout 3600 - -set core 0 -set wordsperline 1 - -if {$::argc > 1} { - set core [lindex $::argv 0] - set wordsperline [lindex $::argv 1] -} elseif {$::argc > 0} { - set core [lindex $::argv 0] -} - -spawn nios2-terminal --instance 0 -expect -ex "connected to hardware target" - -send "@[format %x [expr $core * 32768 / $wordsperline]]\n" - -while {[gets stdin line] >= 0} { - send "$line\n" - expect -ex "$line" -} - -# It's likely we ended with a very large zero-memory operation. We want to see -# two command bytes echoed back to us in order to guarantee that the zero -# operation has completely flushed to memory. -send "?" -expect -ex "?" -send "?" -expect -ex "?" - -send "\004" -expect -ex "exiting due to ^D on remote" diff --git a/hdl/core.sv b/hdl/core.sv index 6827b8e..587ffeb 100644 --- a/hdl/core.sv +++ b/hdl/core.sv @@ -3,8 +3,6 @@ `define DATA_BITS 12 module core - #( JTAG_INSTANCE = 1 - ) ( input bit clk , input bit reset diff --git a/hdl/rs232.sv b/hdl/rs232.sv index 31beb1e..ef53f12 100644 --- a/hdl/rs232.sv +++ b/hdl/rs232.sv @@ -1,6 +1,9 @@ `include "defs.svh" module rs232_tx + #( PARITY = 0 + , STOP_BITS = 2 + ) ( input bit clock , input bit reset @@ -9,6 +12,7 @@ module rs232_tx , input uart_byte_t out_data , output bit tx + , input bit cts ); bit hold_valid; @@ -18,40 +22,45 @@ module rs232_tx (* syn_encoding = "one-hot" *) enum int unsigned { START , DATA - , PARITY - , STOP1 - , STOP2 + , PARITY_BIT + , STOP } state; - bit [$clog2(`UART_BYTE_BITS):0] data_bits; + bit [$clog2($bits(uart_byte_t)):0] data_bits; + bit [$clog2(STOP_BITS):0] stop_bits; always @(posedge clock, posedge reset) begin if (reset) begin out_ready = 0; tx = 1; hold_valid = 0; - parity = 0; + parity = PARITY; state = state.first; data_bits = 0; + stop_bits = 0; end else begin if (out_ready && out_valid) begin hold_valid = 1; hold = out_data; - parity = 0; + parity = PARITY; state = state.first; data_bits = 0; + stop_bits = 0; end if (hold_valid) begin case (state) START: begin - tx = 0; - state = state.next; + if (!cts) begin + tx = 0; + state = state.next; + end end DATA: begin - tx = hold[data_bits]; + tx = hold[0]; + hold = hold >> 1; parity = parity ^ tx; if (data_bits == `UART_BYTE_BITS-1) state = state.next; @@ -59,20 +68,19 @@ module rs232_tx ++data_bits; end - PARITY: begin + PARITY_BIT: begin tx = parity; state = state.next; end - STOP1: begin - tx = 1; - state = state.next; - end - - STOP2: begin - hold_valid = 0; + STOP: begin tx = 1; - state = state.next; + if (stop_bits == STOP_BITS-1) begin + hold_valid = 0; + state = state.next; + end else begin + ++stop_bits; + end end endcase @@ -87,69 +95,94 @@ module rs232_tx endmodule module rs232_rx - #( OVERSAMPLE = 0 + #( PARITY = 0 + , OVERSAMPLE = 0 ) ( input bit clock , input bit reset + , output bit clock_out + , input bit in_ready , output bit in_valid , output uart_byte_t in_data , input bit rx + , output bit rts ); (* syn_encoding = "one-hot" *) enum int unsigned { START + , ALIGN , DATA - , PARITY + , PARITY_BIT , STOP + , REALSTOP } state; uart_byte_t buffer; bit [$clog2(`UART_BYTE_BITS):0] data_bits; bit parity; - bit [$clog2(OVERSAMPLE+1):0] sample; + bit [$clog2(OVERSAMPLE+1)+2:0] sample; + bit [$clog2(OVERSAMPLE+1):0] clock_counter; always @(posedge clock, posedge reset) begin if (reset) begin + clock_out = 0; in_valid = 0; + rts = 1; state = state.first; buffer = 0; data_bits = 0; - parity = 0; + parity = PARITY; sample = 0; + clock_counter = 0; end else begin automatic bit ok = 0; if (in_ready && in_valid) in_valid = 0; - if (state == state.first) begin + if (state == state.first || state == state.last) begin + sample = 0; ok = 1; end else begin ++sample; - if (sample == OVERSAMPLE+1) begin + if (sample > OVERSAMPLE) begin sample = 0; ok = 1; end end + ++clock_counter; + if (clock_counter > OVERSAMPLE) begin + clock_out = 1; + clock_counter = 0; + end else begin + clock_out = 0; + end + if (ok) begin case (state) START: begin if (rx == 0) begin state = state.next; + if (OVERSAMPLE == 0) + state = state.next; + sample = (OVERSAMPLE+1) / 2; buffer = 0; data_bits = 0; - parity = 0; + parity = PARITY; end end + ALIGN: state = state.next; + DATA: begin - buffer[data_bits] = rx; + buffer = buffer >> 1; + buffer[`UART_BYTE_BITS-1] = rx; parity = parity ^ rx; if (data_bits == `UART_BYTE_BITS-1) state = state.next; @@ -157,24 +190,34 @@ module rs232_rx ++data_bits; end - PARITY: begin + PARITY_BIT: begin parity = parity ^ rx; - if (parity == 0) - state = state.next; - else - state = state.first; + state = state.next; end STOP: begin - if (!in_valid && rx == 1) begin + if (!in_valid && rx == 1 && parity == 0) begin in_valid = 1; in_data = buffer; end - state = state.next; + if (rx == 1 && parity == 0) begin + clock_counter = (OVERSAMPLE+1)/2; + end + if (rx == 1) + state = state.first; + else + state = state.next; + end + + REALSTOP: begin + if (rx == 1) + state = state.first; end endcase end + + rts = !(state == state.first && !in_valid); end end diff --git a/hdl/top.sv b/hdl/top.sv index ffe6656..6512185 100644 --- a/hdl/top.sv +++ b/hdl/top.sv @@ -7,10 +7,14 @@ module top , inout wire [10:1] gpioa , inout wire [28:13] gpiob , inout wire [40:31] gpioc - , output wire clock_out , output wire rs232_tx , input wire rs232_rx + , output wire rs232_rts + , input wire rs232_cts + + , output wire debug_tx + , output wire debug_rx , output bit ram_resetn , output bit ram_csn @@ -20,11 +24,14 @@ module top , inout bit [7:0] ram_data ); + assign debug_tx = rs232_tx; + assign debug_rx = rs232_rx; + bit internal_clock; bit internal_reset; pll - #( .MULTIPLY_BY(3) - , .DIVIDE_BY(5) + #( .MULTIPLY_BY(1) + , .DIVIDE_BY(1) ) fastpll ( .native_clk(clock) , .reset_n(resetn) @@ -32,51 +39,36 @@ module top , .reset(internal_reset) ); - //assign clock_out = internal_clock; - - bit rs232_tx_clock = 0; - bit [17:0] rs232_tx_div = 0; - always @(posedge internal_clock) begin + bit rs232_tx_clock; + bit rs232_tx_reset = 1; + always @(posedge rs232_tx_clock) begin if (internal_reset) begin - rs232_tx_clock = 0; - rs232_tx_div = 0; + rs232_tx_reset = 1; end else begin - if (++rs232_tx_div == 130) begin // (30MHz/2)/115200 - ++rs232_tx_clock; - rs232_tx_div = 0; - end + rs232_tx_reset = 0; end end - assign clock_out = rs232_tx_clock; - - bit rs232_rx_clock = 0; - bit [4:0] rs232_rx_div = 0; - always @(posedge internal_clock) begin - if (internal_reset) begin - rs232_rx_clock = 0; - rs232_rx_div = 0; - end else begin - if (++rs232_rx_div == 16) begin // (30MHz/2)/(115200*8) - ++rs232_rx_clock; - rs232_rx_div = 0; - end - end - end + bit rs232_rx_clock; + assign rs232_rx_clock = internal_clock; bit wire_tx_ready; bit wire_tx_valid; uart_byte_t wire_tx_data; - rs232_tx wiretx + rs232_tx + #( .PARITY(1) + , .STOP_BITS(2) + ) wiretx ( .clock(rs232_tx_clock) - , .reset(internal_reset) + , .reset(rs232_tx_reset) , .out_ready(wire_tx_ready) , .out_valid(wire_tx_valid) , .out_data(wire_tx_data) , .tx(rs232_tx) + , .cts(rs232_cts) ); bit rs232_tx_ready; @@ -88,6 +80,7 @@ module top ) fifotx ( .clock_in(internal_clock) , .clock_out(rs232_tx_clock) + , .reset(internal_reset || rs232_tx_reset) , .in_ready(rs232_tx_ready) , .in_valid(rs232_tx_valid) @@ -103,16 +96,20 @@ module top uart_byte_t wire_rx_data; rs232_rx - #( .OVERSAMPLE(7) + #( .PARITY(1) + , .OVERSAMPLE(433) ) wirerx ( .clock(rs232_rx_clock) , .reset(internal_reset) + , .clock_out(rs232_tx_clock) + , .in_ready(wire_rx_ready) , .in_valid(wire_rx_valid) , .in_data(wire_rx_data) , .rx(rs232_rx) + , .rts(rs232_rts) ); bit rs232_rx_ready; @@ -122,8 +119,9 @@ module top fifo #( .WIDTH_BITS($bits(uart_byte_t)) ) fiforx - ( .clock_in(rs232_clock) + ( .clock_in(rs232_rx_clock) , .clock_out(internal_clock) + , .reset(internal_reset) , .in_ready(wire_rx_ready) , .in_valid(wire_rx_valid) @@ -182,6 +180,7 @@ module top bit [7:0] ram_data_out; assign ram_data = ram_data_oe ? ram_data_out : 8'bZ; +/* alt_jtag_atlantic #( .INSTANCE_ID(0) , .LOG2_RXFIFO_DEPTH(10) @@ -199,6 +198,15 @@ module top , .t_dav(ram_rx_ready) , .t_ena(ram_rx_valid) ); +*/ + + assign ram_tx_ready = rs232_tx_ready; + assign rs232_tx_valid = ram_tx_valid; + assign rs232_tx_data = ram_tx_data; + + assign rs232_rx_ready = ram_rx_ready; + assign ram_rx_valid = rs232_rx_valid; + assign ram_rx_data = rs232_rx_data; echo_arbiter uart0arb ( .clock(internal_clock) @@ -445,9 +453,7 @@ module top bit rx_valid; uart_byte_t rx_data; - core - #( .JTAG_INSTANCE(1+i) - ) cpu + core cpu ( .clk(internal_clock) , .reset(internal_reset) @@ -507,33 +513,23 @@ module top , .led_link(local_led_link[i]) ); - if (i == `NUM_PDPS-1) begin - assign tx_ready = rs232_tx_ready; - assign rs232_tx_valid = tx_valid; - assign rs232_tx_data = tx_data; - - assign rs232_rx_ready = rx_ready; - assign rx_valid = rs232_rx_valid; - assign rx_data = rs232_rx_data; - end else begin - alt_jtag_atlantic - #( .INSTANCE_ID(1+i) - , .LOG2_RXFIFO_DEPTH(10) - , .LOG2_TXFIFO_DEPTH(10) - , .SLD_AUTO_INSTANCE_INDEX("NO") - ) uart - ( .clk(internal_clock) - , .rst_n(!internal_reset) - - , .r_dat(tx_data) - , .r_val(tx_valid) - , .r_ena(tx_ready) - - , .t_dat(rx_data) - , .t_dav(rx_ready) - , .t_ena(rx_valid) - ); - end + alt_jtag_atlantic + #( .INSTANCE_ID(i) + , .LOG2_RXFIFO_DEPTH(10) + , .LOG2_TXFIFO_DEPTH(10) + , .SLD_AUTO_INSTANCE_INDEX("NO") + ) uart + ( .clk(internal_clock) + , .rst_n(!internal_reset) + + , .r_dat(tx_data) + , .r_val(tx_valid) + , .r_ena(tx_ready) + + , .t_dat(rx_data) + , .t_dav(rx_ready) + , .t_ena(rx_valid) + ); end diff --git a/tcl/init.tcl b/tcl/init.tcl index 3b4e2ed..ea3f64c 100644 --- a/tcl/init.tcl +++ b/tcl/init.tcl @@ -64,12 +64,59 @@ iopin gpioc[38] P2 iopin gpioc[39] P1 iopin gpioc[40] R1 +# Arduino shield pins: (seen from top) +# +# https://upload.wikimedia.org/wikipedia/commons/c/c9/Pinout_of_ARDUINO_Board_and_ATMega328PU.svg +# +# SCL +# SDA +# AREF +# GND +# NC 13 +# IOREF 12 +# RESET 11 +# 3.3V 10 +# 5V 9 +# GND 8 +# GND +# VIN 7 +# 6 +# A0 5 +# A1 4 +# A2 3 +# A3 2 +# A4 1(RX) +# A5 0(TX) +# +# Equivalent FPGA pins: +# +# N2 or D8 (?) +# N1 or C8 (?) +# - +# GND +# - L1 +# - L2 +# L3 K1 +# 3.3V L4 +# 5V K5 +# GND K2 +# GND +# - J1 +# J2 +# - G1 +# - G2 +# - D1 +# - F3 +# - C2 +# - B1 + pin rs232_rx B1 pin rs232_tx C2 pin rs232_rts F3 pin rs232_cts D1 -pin clock_out L1 +pin debug_rx K2 +pin debug_tx K5 rampin ram_data[0] T12 rampin ram_data[1] T13 diff --git a/tool/con.c b/tool/con.c new file mode 100644 index 0000000..8522786 --- /dev/null +++ b/tool/con.c @@ -0,0 +1,106 @@ +/* con device */ + +#include +#include +#include +#include +#include +#include +#include + +struct termios save; + +void +host(int fd) +{ + int cc; + char buf[128]; + + for (;;) { + cc = read(fd, buf, sizeof buf); +#ifdef HEX + if (cc < 0) + break; + for (int i = 0; i < cc; ++i) { + char hex[3]; + sprintf(hex, "%02X", (unsigned char) buf[i]); + write(1, hex, 2); + } +#else + if (cc > 0) + write(1, buf, cc); + else if (cc < 0) + break; +#endif + } +} + +void +hup(int signo) +{ + write(1, "HUP\n", 4); + tcsetattr(0, TCSADRAIN, &save); + _exit(0); +} + +void +user(int fd) +{ + char c, last = '\r'; + + signal(SIGHUP, hup); + for (;;) + while (read(0, &c, 1) > 0) { + if (c == '~' && (last == '\n' || last == '\r' + || last == 4)) { + read(0, &c, 1); + if (c == '.') + return; + } + write(fd, &c, 1); + last = c; + } +} + +int +main(int argc, char *argv[]) +{ + int fd; + pid_t pid; + struct termios t; + + if (argc != 2) + exit(1); + + fd = open(argv[1], O_RDWR | O_NDELAY); + if (fd < 0) + exit(2); + fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) & ~O_NDELAY); + tcgetattr(fd, &t); + t.c_cc[VMIN] = 1; + t.c_cc[VTIME] = 0; + tcsetattr(fd, TCSADRAIN, &t); + + tcgetattr(0, &t); + save = t; + t.c_iflag = 0; + t.c_oflag = 0; + t.c_lflag = 0; + t.c_cc[VMIN] = 1; + t.c_cc[VTIME] = 0; + tcsetattr(0, TCSADRAIN, &t); + + if ((pid = fork()) > 0) { + user(fd); + } else if (pid == 0) { + host(fd); + } else { + perror("fork"); + _exit(EXIT_FAILURE); + } + + tcsetattr(0, TCSADRAIN, &save); + kill(pid, SIGKILL); + close(fd); + _exit(EXIT_SUCCESS); +} diff --git a/tool/connect b/tool/connect index 536db5b..f900e0c 100755 --- a/tool/connect +++ b/tool/connect @@ -1,21 +1,24 @@ #!/bin/bash -set -eux +set -eu -OLD_SETTINGS="$(stty -F /dev/ttyUSB0 -g)" +OLD_SETTINGS="$(stty -g -F /dev/ttyUSB0)" stty -F /dev/ttyUSB0 \ 115200 \ -drain \ -clocal \ cread \ - -crtscts \ + crtscts \ cs8 \ cstopb \ -hup \ parenb \ - -parodd \ + parodd \ + -cmspar \ -igncr \ + ignpar \ + -parmrk \ inpck \ -istrip \ -ixany \ @@ -26,4 +29,4 @@ stty -F /dev/ttyUSB0 \ con /dev/ttyUSB0 || true -stty -F "$OLD_SETTINGS" +stty -drain "$OLD_SETTINGS" -F /dev/ttyUSB0 2> /dev/null diff --git a/tool/download.tcl b/tool/download.tcl new file mode 100755 index 0000000..c0dca55 --- /dev/null +++ b/tool/download.tcl @@ -0,0 +1,33 @@ +#!/usr/bin/env expect + +set timeout 3600 + +set core 0 +set wordsperline 1 + +if {$::argc > 1} { + set core [lindex $::argv 0] + set wordsperline [lindex $::argv 1] +} elseif {$::argc > 0} { + set core [lindex $::argv 0] +} + +spawn tool/connect + +send "@[format %x [expr $core * 32768 / $wordsperline]]\n" + +while {[gets stdin line] >= 0} { + send "$line\n" + expect -ex "$line\n" +} + +# It's likely we ended with a very large zero-memory operation. We want to see +# two command bytes echoed back to us in order to guarantee that the zero +# operation has completely flushed to memory. +send "?" +expect -ex "?" +send "?" +expect -ex "?" + +send "\n~." +wait diff --git a/tool/p8bin2uart.c b/tool/p8bin2uart.c index de5a328..88c19cd 100644 --- a/tool/p8bin2uart.c +++ b/tool/p8bin2uart.c @@ -131,7 +131,7 @@ load(FILE *fp) return v; } -#define MAX_LINE_SIZE 63 +#define MAX_LINE_SIZE 127 char buf[MAX_LINE_SIZE * 2] = {0}; -- cgit v1.2.3