Merge branch 'uart-irq' into 'master'

Add UART Receive logic

See merge request bslathi19/super6502!5
This commit is contained in:
Byron Lathi
2022-03-14 21:56:27 +00:00
6 changed files with 134 additions and 31 deletions

View File

@@ -117,6 +117,8 @@ SevenSeg segs(
.HEX0(HEX0), .HEX1(HEX1), .HEX2(HEX2), .HEX3(HEX3), .HEX4(HEX4), .HEX5(HEX5) .HEX0(HEX0), .HEX1(HEX1), .HEX2(HEX2), .HEX3(HEX3), .HEX4(HEX4), .HEX5(HEX5)
); );
logic uart_irq;
uart uart( uart uart(
.clk_50(clk_50), .clk_50(clk_50),
.clk(clk), .clk(clk),
@@ -127,6 +129,7 @@ uart uart(
.addr(cpu_addr[1:0]), .addr(cpu_addr[1:0]),
.RXD(UART_RXD), .RXD(UART_RXD),
.TXD(UART_TXD), .TXD(UART_TXD),
.irq(uart_irq),
.data_out(uart_data_out) .data_out(uart_data_out)
); );
@@ -135,8 +138,13 @@ always_ff @(posedge clk_50) begin
irq_data_out <= '0; irq_data_out <= '0;
else if (irq_cs && ~cpu_rwb) else if (irq_cs && ~cpu_rwb)
irq_data_out <= irq_data_out & cpu_data_in; 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 end

View File

@@ -12,6 +12,7 @@ module uart(
output logic TXD, output logic TXD,
output logic irq,
output logic [7:0] data_out output logic [7:0] data_out
); );
@@ -22,18 +23,24 @@ logic [7:0] rx_buf;
logic [7:0] status; logic [7:0] status;
logic tx_flag; logic tx_flag;
logic rx_flag;
logic tx_flag_set; logic tx_flag_set;
logic tx_flag_clear; logic tx_flag_clear;
logic rx_flag_set;
logic rx_flag_clear;
assign status[0] = tx_flag | tx_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 always_ff @(posedge clk) begin
if (rst) begin if (rst) begin
tx_flag_set <= '0; tx_flag_set <= '0;
rx_flag_clear <= '0;
tx_buf <= '0; tx_buf <= '0;
rx_buf <= '0; status[7:2] <= '0;
status[7:1] <= '0;
end end
if (cs) begin if (cs) begin
@@ -52,26 +59,31 @@ always_ff @(posedge clk) begin
tx_flag_set <= '1; tx_flag_set <= '1;
else else
tx_flag_set <= '0; tx_flag_set <= '0;
if (rw & cs && addr == 0)
rx_flag_clear <= '1;
else
rx_flag_clear <= '0;
end end
// state controller // tx state controller
typedef enum bit [2:0] {START, DATA, PARITY, STOP, IDLE} macro_t; typedef enum bit [2:0] {START, DATA, PARITY, STOP, IDLE} macro_t;
struct packed { struct packed {
macro_t macro; macro_t macro;
logic [3:0] count; logic [3:0] count;
} state, next_state; } tx_state, tx_next_state, rx_state, rx_next_state;
localparam logic [3:0] maxcount = 4'h7; localparam logic [3:0] maxcount = 4'h7;
// baud rate: 9600 // baud rate: 9600
localparam baud = 9600; localparam baud = 9600;
localparam count = (50000000/baud)-1; localparam count = (50000000/baud)-1;
logic [14:0] clkdiv; logic [14:0] tx_clkdiv;
always_ff @(posedge clk_50) begin always_ff @(posedge clk_50) begin
if (rst) begin if (rst) begin
clkdiv <= 0; tx_clkdiv <= 0;
state.macro <= IDLE; tx_state.macro <= IDLE;
state.count <= 3'b0; tx_state.count <= 3'b0;
tx_flag <= '0; tx_flag <= '0;
end else begin end else begin
if (tx_flag_set) if (tx_flag_set)
@@ -79,43 +91,43 @@ always_ff @(posedge clk_50) begin
else if (tx_flag_clear) else if (tx_flag_clear)
tx_flag <= '0; tx_flag <= '0;
if (clkdiv == count) begin if (tx_clkdiv == count) begin
clkdiv <= 0; tx_clkdiv <= 0;
state <= next_state; tx_state <= tx_next_state;
end else begin end else begin
clkdiv <= clkdiv + 15'b1; tx_clkdiv <= tx_clkdiv + 15'b1;
end end
end end
end end
always_comb begin always_comb begin
next_state = state; tx_next_state = tx_state;
unique case (state.macro) unique case (tx_state.macro)
START: begin START: begin
next_state.macro = DATA; tx_next_state.macro = DATA;
next_state.count = 3'b0; tx_next_state.count = 3'b0;
end end
DATA: begin DATA: begin
if (state.count == maxcount) begin if (tx_state.count == maxcount) begin
next_state.macro = STOP; // or PARITY tx_next_state.macro = STOP; // or PARITY
next_state.count = 3'b0; tx_next_state.count = 3'b0;
end else begin end else begin
next_state.count = state.count + 3'b1; tx_next_state.count = tx_state.count + 3'b1;
next_state.macro = DATA; tx_next_state.macro = DATA;
end end
end end
PARITY: begin PARITY: begin
end end
STOP: begin STOP: begin
next_state.macro = IDLE; tx_next_state.macro = IDLE;
next_state.count = '0; tx_next_state.count = '0;
end end
IDLE: begin IDLE: begin
if (tx_flag) if (tx_flag)
next_state.macro = START; tx_next_state.macro = START;
else else
next_state.macro = IDLE; tx_next_state.macro = IDLE;
end end
default:; default:;
@@ -126,12 +138,12 @@ always_comb begin
TXD = '1; TXD = '1;
tx_flag_clear = '0; tx_flag_clear = '0;
unique case (state.macro) unique case (tx_state.macro)
START: begin START: begin
TXD = '0; TXD = '0;
end end
DATA: begin DATA: begin
TXD = tx_buf[state.count]; TXD = tx_buf[tx_state.count];
end end
PARITY: begin PARITY: begin
@@ -148,4 +160,69 @@ always_comb begin
endcase endcase
end 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 endmodule

View File

@@ -4,6 +4,7 @@
#include <stdint.h> #include <stdint.h>
#define BUTTON (1 << 0) #define BUTTON (1 << 0)
#define UART (1 << 1)
void irq_int(); void irq_int();
void nmi_int(); void nmi_int();

View File

@@ -2,12 +2,14 @@
#include <stdint.h> #include <stdint.h>
#include "interrupt.h" #include "interrupt.h"
#include "uart.h"
// This is defined in main.c // This is defined in main.c
void puts(const char* s); void puts(const char* s);
void handle_irq() { void handle_irq() {
uint8_t status; uint8_t status;
char c;
puts("Interrupt Detected!\n"); puts("Interrupt Detected!\n");
@@ -17,4 +19,12 @@ void handle_irq() {
puts("Button Interrupt!\n"); puts("Button Interrupt!\n");
irq_set_status(status & ~BUTTON); irq_set_status(status & ~BUTTON);
} }
if (status & UART) {
puts("UART Interrupt!\n");
c = uart_rxb();
puts("Received: ");
puts(&c);
puts("\n");
irq_set_status(status & ~UART);
}
} }

View File

@@ -6,6 +6,8 @@
void uart_txb(uint8_t val); void uart_txb(uint8_t val);
void uart_txb_block(uint8_t val); void uart_txb_block(uint8_t val);
uint8_t uart_rxb();
uint8_t uart_status(); uint8_t uart_status();
#endif #endif

View File

@@ -2,8 +2,8 @@
.importzp sp, sreg .importzp sp, sreg
.export _uart_txb .export _uart_txb, _uart_txb_block
.export _uart_txb_block .export _uart_rxb
.export _uart_status .export _uart_status
.autoimport on .autoimport on
@@ -23,6 +23,11 @@ _uart_txb_block:
bne @1 bne @1
rts rts
_uart_rxb:
lda UART_RXB ; Read value
ldx #$00
rts
_uart_status: _uart_status:
lda UART_STATUS lda UART_STATUS
ldx #$00 ldx #$00