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)
);
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

View File

@@ -12,6 +12,7 @@ module uart(
output logic TXD,
output logic irq,
output logic [7:0] data_out
);
@@ -22,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
@@ -52,26 +59,31 @@ 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
// 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
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;
state.macro <= IDLE;
state.count <= 3'b0;
tx_clkdiv <= 0;
tx_state.macro <= IDLE;
tx_state.count <= 3'b0;
tx_flag <= '0;
end else begin
if (tx_flag_set)
@@ -79,43 +91,43 @@ always_ff @(posedge clk_50) begin
else if (tx_flag_clear)
tx_flag <= '0;
if (clkdiv == count) begin
clkdiv <= 0;
state <= next_state;
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
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:;
@@ -126,12 +138,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
@@ -148,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

View File

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

View File

@@ -2,12 +2,14 @@
#include <stdint.h>
#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");
@@ -17,4 +19,12 @@ void handle_irq() {
puts("Button Interrupt!\n");
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_block(uint8_t val);
uint8_t uart_rxb();
uint8_t uart_status();
#endif

View File

@@ -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