Merge branch 'uart-irq' into 'master'
Add UART Receive logic See merge request bslathi19/super6502!5
This commit is contained in:
@@ -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
|
||||||
|
|
||||||
|
|||||||
131
hw/fpga/uart.sv
131
hw/fpga/uart.sv
@@ -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
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
10
sw/irq.c
10
sw/irq.c
@@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user