From 1e3961030578c69180992f46096ef24ea2f4c3a5 Mon Sep 17 00:00:00 2001 From: Jan Tuomi Date: Wed, 19 Mar 2025 22:24:47 +0200 Subject: Implement working serial repl --- atk16_fpga/.gitignore | 1 + atk16_fpga/Makefile | 5 +- atk16_fpga/gen_data_strings_v.py | 25 +++ atk16_fpga/hardware.dot | 70 ++++++ atk16_fpga/hardware.svg | 463 +++++++++++++++++++++++++++++++++++++++ atk16_fpga/top.v | 135 +++++++++--- 6 files changed, 669 insertions(+), 30 deletions(-) create mode 100755 atk16_fpga/gen_data_strings_v.py create mode 100644 atk16_fpga/hardware.dot create mode 100644 atk16_fpga/hardware.svg (limited to 'atk16_fpga') diff --git a/atk16_fpga/.gitignore b/atk16_fpga/.gitignore index bba1e09..5fdef91 100644 --- a/atk16_fpga/.gitignore +++ b/atk16_fpga/.gitignore @@ -4,3 +4,4 @@ *.out *.vcd *.dblite +data_strings.v diff --git a/atk16_fpga/Makefile b/atk16_fpga/Makefile index cada068..2a7e495 100644 --- a/atk16_fpga/Makefile +++ b/atk16_fpga/Makefile @@ -1,6 +1,9 @@ BIN ?= program.bin -hardware.bin: top.v +data_strings.v: gen_data_strings_v.py + python gen_data_strings_v.py > data_strings.v + +hardware.bin: top.v data_strings.v apio verify apio test apio build diff --git a/atk16_fpga/gen_data_strings_v.py b/atk16_fpga/gen_data_strings_v.py new file mode 100755 index 0000000..afb374e --- /dev/null +++ b/atk16_fpga/gen_data_strings_v.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python + +def gen(name, message): + for line in message.split('\n'): + print(f'// {line}') + + print(f"`define LEN_{name} {len(message)}") + print(f"`define STR_{name} \\") + print(f" reg [7:0] str_{name} [0:`LEN_{name}-1]; \\") + print(f" initial begin \\") + for i, c in enumerate(message): + print(f" str_{name}[{i}] = 8'd{ord(c)}; \\") + + print(f" end") + print("") + +gen("welcome", "Welcome to the ATK16 serial programmer. Press 'h' for help.\n") +gen("help", + "Help:\n" + + " h - help\n" + + " p - program\n" +) +gen("program_ready", + "Ready to receive program data. Reset device after transmission.\n" +) diff --git a/atk16_fpga/hardware.dot b/atk16_fpga/hardware.dot new file mode 100644 index 0000000..e1f4c58 --- /dev/null +++ b/atk16_fpga/hardware.dot @@ -0,0 +1,70 @@ +digraph "top" { +label="top"; +rankdir="LR"; +remincross=true; +n13 [ shape=diamond, label="rx_done", color="black", fontcolor="black" ]; +n14 [ shape=diamond, label="data_to_receive", color="black", fontcolor="black" ]; +n15 [ shape=diamond, label="state", color="black", fontcolor="black" ]; +n16 [ shape=diamond, label="letter_a", color="black", fontcolor="black" ]; +n17 [ shape=diamond, label="tx_busy", color="black", fontcolor="black" ]; +n18 [ shape=diamond, label="data_to_send", color="black", fontcolor="black" ]; +n19 [ shape=diamond, label="start_tx", color="black", fontcolor="black" ]; +n20 [ shape=octagon, label="LED2", color="black", fontcolor="black" ]; +n21 [ shape=octagon, label="LED1", color="black", fontcolor="black" ]; +n22 [ shape=octagon, label="SERIAL_TX", color="black", fontcolor="black" ]; +n23 [ shape=octagon, label="SERIAL_RX", color="black", fontcolor="black" ]; +n24 [ shape=octagon, label="SYSCLK", color="black", fontcolor="black" ]; +v0 [ label="1" ]; +v1 [ label="0" ]; +c29 [ shape=record, label="{{ A| B| S}|$6\n$mux|{ Y}}", ]; +c30 [ shape=record, label="{{ A| B}|$5\n$eq|{ Y}}", ]; +c35 [ shape=record, label="{{ clk| data| done| rx}|uart_rx_inst\nuart_rx|{}}", ]; +c39 [ shape=record, label="{{ busy| clk| data| start| tx}|uart_inst\nuart_tx|{}}", ]; +c40 [ shape=record, label="{{ A}|$4\n$logic_not|{ Y}}", ]; +c41 [ shape=record, label="{{ A}|$3\n$logic_not|{ Y}}", ]; +c42 [ shape=record, label="{{ A}|$2\n$logic_not|{ Y}}", ]; +p2 [shape=box, style=rounded, label="PROC $10\ntop.v:28.15-28.35", ]; +p3 [shape=box, style=rounded, label="PROC $9\ntop.v:19.15-19.31", ]; +p4 [shape=box, style=rounded, label="PROC $8\ntop.v:15.15-15.34", ]; +p5 [shape=box, style=rounded, label="PROC $7\ntop.v:13.9-13.24", ]; +p6 [shape=box, style=rounded, label="PROC $1\ntop.v:31.5-70.8", ]; +x7 [ shape=record, style=rounded, label=" 0:0 - 0:0 ", colorscheme="dark28", color="8", fontcolor="8" ]; +x8 [shape=point, ]; +n13:e -> c35:p33:w [colorscheme="dark28", color="4", fontcolor="4", label=""]; +n13:e -> c42:p25:w [colorscheme="dark28", color="4", fontcolor="4", label=""]; +n13:e -> p6:w [colorscheme="dark28", color="4", fontcolor="4", label=""]; +n14:e -> c30:p25:w [colorscheme="dark28", color="4", fontcolor="4", style="setlinewidth(3)", label=""]; +n14:e -> c35:p32:w [colorscheme="dark28", color="4", fontcolor="4", style="setlinewidth(3)", label=""]; +n14:e -> p6:w [colorscheme="dark28", color="4", fontcolor="4", style="setlinewidth(3)", label=""]; +p2:e -> n15:w [colorscheme="dark28", color="3", fontcolor="3", style="setlinewidth(3)", label=""]; +p6:e -> n15:w [colorscheme="dark28", color="3", fontcolor="3", style="setlinewidth(3)", label=""]; +n15:e -> p6:w [colorscheme="dark28", color="3", fontcolor="3", style="setlinewidth(3)", label=""]; +p3:e -> n16:w [colorscheme="dark28", color="2", fontcolor="2", style="setlinewidth(3)", label=""]; +n16:e -> c30:p26:w [colorscheme="dark28", color="2", fontcolor="2", style="setlinewidth(3)", label=""]; +n17:e -> c39:p36:w [colorscheme="dark28", color="1", fontcolor="1", label=""]; +n17:e -> c40:p25:w [colorscheme="dark28", color="1", fontcolor="1", label=""]; +n17:e -> c41:p25:w [colorscheme="dark28", color="1", fontcolor="1", label=""]; +p4:e -> n18:w [colorscheme="dark28", color="1", fontcolor="1", style="setlinewidth(3)", label=""]; +p6:e -> n18:w [colorscheme="dark28", color="1", fontcolor="1", style="setlinewidth(3)", label=""]; +n18:e -> c39:p32:w [colorscheme="dark28", color="1", fontcolor="1", style="setlinewidth(3)", label=""]; +n18:e -> p6:w [colorscheme="dark28", color="1", fontcolor="1", style="setlinewidth(3)", label=""]; +p5:e -> n19:w [colorscheme="dark28", color="6", fontcolor="6", label=""]; +p6:e -> n19:w [colorscheme="dark28", color="6", fontcolor="6", label=""]; +n19:e -> c39:p37:w [colorscheme="dark28", color="6", fontcolor="6", label=""]; +n19:e -> p6:w [colorscheme="dark28", color="6", fontcolor="6", label=""]; +x8:e -> n20:w [colorscheme="dark28", color="2", fontcolor="2", label=""]; +x7:e -> n21:w [colorscheme="dark28", color="2", fontcolor="2", label=""]; +n21:e -> x8:w [colorscheme="dark28", color="2", fontcolor="2", label=""]; +n22:e -> c39:p38:w [colorscheme="dark28", color="2", fontcolor="2", label=""]; +n23:e -> c35:p34:w [colorscheme="dark28", color="1", fontcolor="1", label=""]; +n24:e -> c35:p31:w [colorscheme="dark28", color="1", fontcolor="1", label=""]; +n24:e -> c39:p31:w [colorscheme="dark28", color="1", fontcolor="1", label=""]; +n24:e -> p6:w [colorscheme="dark28", color="1", fontcolor="1", label=""]; +c29:p28:e -> x7:s0:w [colorscheme="dark28", color="7", fontcolor="7", style="setlinewidth(3)", label=""]; +c30:p28:e -> c29:p27:w [colorscheme="dark28", color="3", fontcolor="3", label=""]; +c40:p28:e -> p6:w [colorscheme="dark28", color="7", fontcolor="7", label=""]; +c41:p28:e -> p6:w [colorscheme="dark28", color="6", fontcolor="6", label=""]; +c42:p28:e -> p6:w [colorscheme="dark28", color="1", fontcolor="1", label=""]; +v0:e -> c29:p26:w [colorscheme="dark28", color="5", fontcolor="5", style="setlinewidth(3)", label=""]; +v1:e -> c29:p25:w [colorscheme="dark28", color="7", fontcolor="7", style="setlinewidth(3)", label=""]; +} diff --git a/atk16_fpga/hardware.svg b/atk16_fpga/hardware.svg new file mode 100644 index 0000000..66b633e --- /dev/null +++ b/atk16_fpga/hardware.svg @@ -0,0 +1,463 @@ + + + + + + +top + +top + + +n13 + +rx_done + + + +c35 + +clk + +data + +done + +rx + +uart_rx_inst +uart_rx + + + + + +n13:e->c35:w + + + + + +c42 + +A + +$2 +$logic_not + +Y + + + +n13:e->c42:w + + + + + +p6 + +PROC $1 +top.v:31.5-70.8 + + + +n13:e->p6:w + + + + + +n14 + +data_to_receive + + + +c30 + +A + +B + +$5 +$eq + +Y + + + +n14:e->c30:w + + + + + +n14:e->c35:w + + + + + +n14:e->p6:w + + + + + +n15 + +state + + + +n15:e->p6:w + + + + + +n16 + +letter_a + + + +n16:e->c30:w + + + + + +n17 + +tx_busy + + + +c39 + +busy + +clk + +data + +start + +tx + +uart_inst +uart_tx + + + + + +n17:e->c39:w + + + + + +c40 + +A + +$4 +$logic_not + +Y + + + +n17:e->c40:w + + + + + +c41 + +A + +$3 +$logic_not + +Y + + + +n17:e->c41:w + + + + + +n18 + +data_to_send + + + +n18:e->c39:w + + + + + +n18:e->p6:w + + + + + +n19 + +start_tx + + + +n19:e->c39:w + + + + + +n19:e->p6:w + + + + + +n20 + +LED2 + + + +n21 + +LED1 + + + +x8 + + + + +n21:e->x8:w + + + + + +n22 + +SERIAL_TX + + + +n22:e->c39:w + + + + + +n23 + +SERIAL_RX + + + +n23:e->c35:w + + + + + +n24 + +SYSCLK + + + +n24:e->c35:w + + + + + +n24:e->c39:w + + + + + +n24:e->p6:w + + + + + +v0 + +1 + + + +c29 + +A + +B + +S + +$6 +$mux + +Y + + + +v0:e->c29:w + + + + + +v1 + +0 + + + +v1:e->c29:w + + + + + +x7 + +0:0 - 0:0 + + + +c29:e->x7:w + + + + + +c30:e->c29:w + + + + + +c40:e->p6:w + + + + + +c41:e->p6:w + + + + + +c42:e->p6:w + + + + + +p2 + +PROC $10 +top.v:28.15-28.35 + + + +p2:e->n15:w + + + + + +p3 + +PROC $9 +top.v:19.15-19.31 + + + +p3:e->n16:w + + + + + +p4 + +PROC $8 +top.v:15.15-15.34 + + + +p4:e->n18:w + + + + + +p5 + +PROC $7 +top.v:13.9-13.24 + + + +p5:e->n19:w + + + + + +p6:e->n15:w + + + + + +p6:e->n18:w + + + + + +p6:e->n19:w + + + + + +x7:e->n21:w + + + + + +x8:e->n20:w + + + + + diff --git a/atk16_fpga/top.v b/atk16_fpga/top.v index c67ade2..6fcbddd 100644 --- a/atk16_fpga/top.v +++ b/atk16_fpga/top.v @@ -9,28 +9,37 @@ module top ( output LED2 ); - // One-cycle pulse to trigger transmission. - reg start_tx = 1'b0; - // Data byte to be transmitted. - reg [7:0] data_to_send = 8'b0; - // Busy flag from the transmitter. - wire tx_busy; - - reg [7:0] letter_a = 8'd97; - // State machine states. - localparam WAIT_RX_BUSY = 3'd0; - localparam WAIT_RX_DONE = 3'd1; - localparam START_TX_1 = 3'd2; - localparam START_TX_2 = 3'd3; - localparam WAIT_TX_DONE = 3'd4; - - reg [2:0] state = WAIT_RX_BUSY; + localparam RX_START = 0; + localparam WAIT_RX_DONE = 1; + localparam START_TX = 2; + localparam TX_IN_PROG = 3; + localparam WAIT_TX_DONE = 4; + localparam HANDLE_RX_DATA = 5; + localparam SEND_STRING_WELCOME = 6; + localparam SEND_STRING_HELP = 7; + localparam SEND_STRING_PROGRAM_READY = 8; + localparam RECEIVE_PROGRAM_DATA = 9; + + reg [7:0] state = SEND_STRING_WELCOME; + + // SEND_WELCOME data + `STR_welcome + reg [7:0] welcome_index = 0; + + // SEND_STRING_HELP data + `STR_help + reg [7:0] help_index = 0; + + // SEND_STRING_PROGRAM_READY data + `STR_program_ready + reg [7:0] program_ready_index = 0; // Synchronous state machine with reset. always @(posedge SYSCLK) begin case (state) - WAIT_RX_BUSY: begin + // STATES FOR RX + RX_START: begin if (!rx_done) begin state <= WAIT_RX_DONE; end @@ -38,21 +47,20 @@ module top ( WAIT_RX_DONE: begin if (rx_done) begin - state <= START_TX_1; + state <= state_after_rx; end end - START_TX_1: begin + // STATES FOR TX + START_TX: begin if (!tx_busy) begin - //data_to_send <= letter_a; - data_to_send <= data_to_receive; start_tx <= 1'b1; // trigger transmission - state <= START_TX_2; + state <= TX_IN_PROG; end end // After issuing the start pulse, wait for tx_busy to go high. - START_TX_2: begin + TX_IN_PROG: begin start_tx <= 1'b0; // ensure pulse is only one cycle. state <= WAIT_TX_DONE; end @@ -60,35 +68,104 @@ module top ( // Wait for the transmitter to finish sending the byte. WAIT_TX_DONE: begin if (!tx_busy) begin - state <= WAIT_RX_BUSY; + state <= state_after_tx; end end - default: state <= WAIT_RX_BUSY; + // STATES FOR PROCESSING DATA + HANDLE_RX_DATA: begin + if (data_rx == cmd_program) begin + state <= SEND_STRING_PROGRAM_READY; + end else if (data_rx == cmd_help) begin + state <= SEND_STRING_HELP; + end else begin + state <= RX_START; + end + end + + SEND_STRING_WELCOME: begin + if (welcome_index < 8'd`LEN_welcome) begin + data_tx <= str_welcome[welcome_index]; + welcome_index <= welcome_index + 1; + state <= START_TX; + state_after_tx <= SEND_STRING_WELCOME; + end else begin + welcome_index <= 0; + state <= RX_START; + state_after_tx <= RX_START; + end + end + + SEND_STRING_HELP: begin + if (help_index < 8'd`LEN_help) begin + data_tx <= str_help[help_index]; + help_index <= help_index + 1; + state <= START_TX; + state_after_tx <= SEND_STRING_HELP; + end else begin + help_index <= 0; + state <= RX_START; + state_after_tx <= RX_START; + end + end + + SEND_STRING_PROGRAM_READY: begin + if (program_ready_index < 8'd`LEN_program_ready) begin + data_tx <= str_program_ready[program_ready_index]; + program_ready_index <= program_ready_index + 1; + state <= START_TX; + state_after_tx <= SEND_STRING_PROGRAM_READY; + end else begin + program_ready_index <= 0; + state <= RECEIVE_PROGRAM_DATA; + state_after_tx <= RX_START; + end + end + + RECEIVE_PROGRAM_DATA: begin + // TODO write data to flash + state <= RX_START; + state_after_rx <= RECEIVE_PROGRAM_DATA; + end + + default: state <= RX_START; endcase end + // One-cycle pulse to trigger transmission. + reg start_tx = 1'b0; + // Data byte to be transmitted. + reg [7:0] data_tx = 8'b0; + // Busy flag from the transmitter. + wire tx_busy; + // State to transition to after TX done. + reg [7:0] state_after_tx = RX_START; + + reg [7:0] cmd_help = 8'd104; // h + reg [7:0] cmd_program = 8'd112; // p + // Instantiate the UART transmitter. uart_tx uart_inst ( .clk (SYSCLK), .start(start_tx), // One-cycle pulse to start transmission. - .data (data_to_send), + .data (data_tx), .tx (SERIAL_TX), .busy (tx_busy) ); - wire [7:0] data_to_receive; + wire [7:0] data_rx; wire rx_done; + reg [7:0] state_after_rx = HANDLE_RX_DATA; uart_rx uart_rx_inst ( .clk (SYSCLK), .rx (SERIAL_RX), - .data(data_to_receive), + .data(data_rx), .done(rx_done) ); - assign LED1 = data_to_receive == letter_a ? 1 : 0; + assign LED1 = data_rx == cmd_program; assign LED2 = LED1; endmodule -- cgit v1.3