From b2344d986e7a2c08148a93b1317a48f55e19443d Mon Sep 17 00:00:00 2001 From: Byron Lathi Date: Mon, 14 Mar 2022 14:57:45 -0500 Subject: [PATCH 1/5] Add UART interrupts Currently an interrupt is triggered any time there is any activity on the UART_RXD line, but later it will only trigger once there is data ready to be read. --- hw/fpga/super6502.sv | 12 ++++++++++-- hw/fpga/uart.sv | 4 ++++ sw/interrupt.h | 1 + sw/irq.c | 4 ++++ 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/hw/fpga/super6502.sv b/hw/fpga/super6502.sv index c564854..af0eae9 100644 --- a/hw/fpga/super6502.sv +++ b/hw/fpga/super6502.sv @@ -117,6 +117,8 @@ SevenSeg segs( .HEX0(HEX0), .HEX1(HEX1), .HEX2(HEX2), .HEX3(HEX3), .HEX4(HEX4), .HEX5(HEX5) ); +logic uart_irq; + uart uart( .clk_50(clk_50), .clk(clk), @@ -127,6 +129,7 @@ uart uart( .addr(cpu_addr[1:0]), .RXD(UART_RXD), .TXD(UART_TXD), + .irq(uart_irq), .data_out(uart_data_out) ); @@ -135,8 +138,13 @@ always_ff @(posedge clk_50) begin irq_data_out <= '0; else if (irq_cs && ~cpu_rwb) irq_data_out <= irq_data_out & cpu_data_in; - else if (~button_1) - irq_data_out <= {irq_data_out[7:1], ~button_1}; + + else begin + if (~button_1) + irq_data_out[0] <= '1; + if (uart_irq) + irq_data_out[1] <= '1; + end end diff --git a/hw/fpga/uart.sv b/hw/fpga/uart.sv index 9c86ffb..2c3e9a0 100644 --- a/hw/fpga/uart.sv +++ b/hw/fpga/uart.sv @@ -12,9 +12,13 @@ module uart( output logic TXD, + output logic irq, output logic [7:0] data_out ); +//Temporary! +assign irq = ~RXD; + //Handle reading and writing registers logic [7:0] tx_buf; diff --git a/sw/interrupt.h b/sw/interrupt.h index ebd5951..d0231aa 100644 --- a/sw/interrupt.h +++ b/sw/interrupt.h @@ -4,6 +4,7 @@ #include #define BUTTON (1 << 0) +#define UART (1 << 1) void irq_int(); void nmi_int(); diff --git a/sw/irq.c b/sw/irq.c index 3aec0c1..a7f7287 100644 --- a/sw/irq.c +++ b/sw/irq.c @@ -17,4 +17,8 @@ void handle_irq() { puts("Button Interrupt!\n"); irq_set_status(status & ~BUTTON); } + if (status & UART) { + puts("UART Interrupt!\n"); + irq_set_status(status & ~UART); + } } \ No newline at end of file From 264263b0d9d4eee03ef617e5ae8011743d951159 Mon Sep 17 00:00:00 2001 From: Byron Lathi Date: Mon, 14 Mar 2022 15:10:59 -0500 Subject: [PATCH 2/5] Change "state" to "tx_state" etc. --- hw/fpga/uart.sv | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/hw/fpga/uart.sv b/hw/fpga/uart.sv index 2c3e9a0..8622c02 100644 --- a/hw/fpga/uart.sv +++ b/hw/fpga/uart.sv @@ -58,12 +58,12 @@ always_ff @(posedge clk) begin tx_flag_set <= '0; end -// state controller +// tx state controller typedef enum bit [2:0] {START, DATA, PARITY, STOP, IDLE} macro_t; struct packed { macro_t macro; logic [3:0] count; -} state, next_state; +} tx_state, tx_next_state, rx_state, rx_next_state; localparam logic [3:0] maxcount = 4'h7; // baud rate: 9600 @@ -74,8 +74,8 @@ logic [14:0] clkdiv; always_ff @(posedge clk_50) begin if (rst) begin clkdiv <= 0; - state.macro <= IDLE; - state.count <= 3'b0; + tx_state.macro <= IDLE; + tx_state.count <= 3'b0; tx_flag <= '0; end else begin if (tx_flag_set) @@ -85,7 +85,7 @@ always_ff @(posedge clk_50) begin if (clkdiv == count) begin clkdiv <= 0; - state <= next_state; + tx_state <= tx_next_state; end else begin clkdiv <= clkdiv + 15'b1; end @@ -93,33 +93,33 @@ always_ff @(posedge clk_50) begin end always_comb begin - next_state = state; + tx_next_state = tx_state; - unique case (state.macro) + unique case (tx_state.macro) START: begin - next_state.macro = DATA; - next_state.count = 3'b0; + tx_next_state.macro = DATA; + tx_next_state.count = 3'b0; end DATA: begin - if (state.count == maxcount) begin - next_state.macro = STOP; // or PARITY - next_state.count = 3'b0; + if (tx_state.count == maxcount) begin + tx_next_state.macro = STOP; // or PARITY + tx_next_state.count = 3'b0; end else begin - next_state.count = state.count + 3'b1; - next_state.macro = DATA; + tx_next_state.count = tx_state.count + 3'b1; + tx_next_state.macro = DATA; end end PARITY: begin end STOP: begin - next_state.macro = IDLE; - next_state.count = '0; + tx_next_state.macro = IDLE; + tx_next_state.count = '0; end IDLE: begin if (tx_flag) - next_state.macro = START; + tx_next_state.macro = START; else - next_state.macro = IDLE; + tx_next_state.macro = IDLE; end default:; @@ -130,12 +130,12 @@ always_comb begin TXD = '1; tx_flag_clear = '0; - unique case (state.macro) + unique case (tx_state.macro) START: begin TXD = '0; end DATA: begin - TXD = tx_buf[state.count]; + TXD = tx_buf[tx_state.count]; end PARITY: begin From ed18b381f3970b5a58fdd618eaf8d1ba88cd546a Mon Sep 17 00:00:00 2001 From: Byron Lathi Date: Mon, 14 Mar 2022 15:22:18 -0500 Subject: [PATCH 3/5] Change "clkdiv" to "tx_clkdiv" --- hw/fpga/uart.sv | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/hw/fpga/uart.sv b/hw/fpga/uart.sv index 8622c02..8eaab40 100644 --- a/hw/fpga/uart.sv +++ b/hw/fpga/uart.sv @@ -69,11 +69,11 @@ localparam logic [3:0] maxcount = 4'h7; // baud rate: 9600 localparam baud = 9600; localparam count = (50000000/baud)-1; -logic [14:0] clkdiv; +logic [14:0] tx_clkdiv; always_ff @(posedge clk_50) begin if (rst) begin - clkdiv <= 0; + tx_clkdiv <= 0; tx_state.macro <= IDLE; tx_state.count <= 3'b0; tx_flag <= '0; @@ -83,11 +83,11 @@ always_ff @(posedge clk_50) begin else if (tx_flag_clear) tx_flag <= '0; - if (clkdiv == count) begin - clkdiv <= 0; + if (tx_clkdiv == count) begin + tx_clkdiv <= 0; tx_state <= tx_next_state; end else begin - clkdiv <= clkdiv + 15'b1; + tx_clkdiv <= tx_clkdiv + 15'b1; end end end From 59d2d4f60156d25622089aa458db1833bbb29b09 Mon Sep 17 00:00:00 2001 From: Byron Lathi Date: Mon, 14 Mar 2022 16:40:04 -0500 Subject: [PATCH 4/5] Add receive logic to UART The UART has a receive buffer which will fill up when it receives bytes. Once the buffer is full, it raises the RX flag until the value is read by the cpu. --- hw/fpga/uart.sv | 83 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 78 insertions(+), 5 deletions(-) diff --git a/hw/fpga/uart.sv b/hw/fpga/uart.sv index 8eaab40..e0a3922 100644 --- a/hw/fpga/uart.sv +++ b/hw/fpga/uart.sv @@ -16,9 +16,6 @@ module uart( output logic [7:0] data_out ); -//Temporary! -assign irq = ~RXD; - //Handle reading and writing registers logic [7:0] tx_buf; @@ -26,18 +23,24 @@ logic [7:0] rx_buf; logic [7:0] status; logic tx_flag; +logic rx_flag; logic tx_flag_set; logic tx_flag_clear; +logic rx_flag_set; +logic rx_flag_clear; assign status[0] = tx_flag | tx_flag_clear; +assign status[1] = rx_flag | rx_flag_set; + +assign irq = status[1]; always_ff @(posedge clk) begin if (rst) begin tx_flag_set <= '0; + rx_flag_clear <= '0; tx_buf <= '0; - rx_buf <= '0; - status[7:1] <= '0; + status[7:2] <= '0; end if (cs) begin @@ -56,6 +59,11 @@ always_ff @(posedge clk) begin tx_flag_set <= '1; else tx_flag_set <= '0; + + if (rw & cs && addr == 0) + rx_flag_clear <= '1; + else + rx_flag_clear <= '0; end // tx state controller @@ -152,4 +160,69 @@ always_comb begin endcase end +//basically in idle state we need to sample RXD very fast, +//then as soon as we detect that RXD is low, we start clkdiv +//going and then go into the start state. + +logic [14:0] rx_clkdiv; + +always_ff @(posedge clk_50) begin + if (rst) begin + rx_buf <= '0; + rx_clkdiv <= 0; + rx_state.macro <= IDLE; + rx_state.count <= 3'b0; + end else begin + if (rx_flag_set) + rx_flag <= '1; + else if (rx_flag_clear) + rx_flag <= '0; + + if (rx_state.macro == IDLE) begin // Sample constantly in idle state + rx_state <= rx_next_state; + rx_clkdiv <= count/15'h2; // offset rx clock by 1/2 phase + end else begin + if (rx_clkdiv == count) begin // other states are as usual + rx_clkdiv <= 0; + rx_state <= rx_next_state; + if (rx_state.macro == DATA) + rx_buf[rx_state.count] = RXD; + end else begin + rx_clkdiv <= rx_clkdiv + 15'b1; + end + end + end +end + +always_comb begin + rx_next_state = rx_state; + rx_flag_set = '0; + + unique case (rx_state.macro) + IDLE: begin + if (~RXD) + rx_next_state.macro = START; + end + START: begin + rx_next_state.macro = DATA; + rx_next_state.count = 3'b0; + end + DATA: begin + if (rx_state.count == maxcount) begin + rx_next_state.macro = STOP; + rx_next_state.count = 3'b0; + end else begin + rx_next_state.count = rx_state.count + 3'b1; + rx_next_state.macro = DATA; + end + end + PARITY: begin + end + STOP: begin + rx_flag_set = '1; + rx_next_state.macro = IDLE; + end + endcase +end + endmodule From 8e161664bb76e6dff7080181cb8ce17bc55e1ba6 Mon Sep 17 00:00:00 2001 From: Byron Lathi Date: Mon, 14 Mar 2022 16:48:24 -0500 Subject: [PATCH 5/5] Add uart_rxb Once you receive a uart interrupt you can call this function to get the received character. --- sw/irq.c | 6 ++++++ sw/uart.h | 2 ++ sw/uart.s | 9 +++++++-- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/sw/irq.c b/sw/irq.c index a7f7287..3608879 100644 --- a/sw/irq.c +++ b/sw/irq.c @@ -2,12 +2,14 @@ #include #include "interrupt.h" +#include "uart.h" // This is defined in main.c void puts(const char* s); void handle_irq() { uint8_t status; + char c; puts("Interrupt Detected!\n"); @@ -19,6 +21,10 @@ void handle_irq() { } if (status & UART) { puts("UART Interrupt!\n"); + c = uart_rxb(); + puts("Received: "); + puts(&c); + puts("\n"); irq_set_status(status & ~UART); } } \ No newline at end of file diff --git a/sw/uart.h b/sw/uart.h index c96b48c..e8f06db 100644 --- a/sw/uart.h +++ b/sw/uart.h @@ -6,6 +6,8 @@ void uart_txb(uint8_t val); void uart_txb_block(uint8_t val); +uint8_t uart_rxb(); + uint8_t uart_status(); #endif \ No newline at end of file diff --git a/sw/uart.s b/sw/uart.s index 80b5563..dd23778 100644 --- a/sw/uart.s +++ b/sw/uart.s @@ -2,8 +2,8 @@ .importzp sp, sreg -.export _uart_txb -.export _uart_txb_block +.export _uart_txb, _uart_txb_block +.export _uart_rxb .export _uart_status .autoimport on @@ -23,6 +23,11 @@ _uart_txb_block: bne @1 rts +_uart_rxb: + lda UART_RXB ; Read value + ldx #$00 + rts + _uart_status: lda UART_STATUS ldx #$00