Files
super6502/hw/efinix_fpga/uart.sv
Byron Lathi fcae23785e Throw everything up
I think that previously, I had not actually commited any of this to git.
This adds all of the new effinix stuff that I had been working on for
months.

The gist of all of this is that the intel fpga is expensive and does not
exist, whereas the effinix ones are not as expensive and more existant.
This redoes the project to use the dev board, as well as a custom board
that I may or may not make.
2022-10-04 17:15:49 -05:00

229 lines
5.1 KiB
Systemverilog

module uart(
input clk_50,
input clk,
input rst,
input cs,
input rw,
input [7:0] data_in,
input [1:0] addr,
input RXD,
output logic TXD,
output logic irq,
output logic [7:0] data_out
);
//Handle reading and writing registers
logic [7:0] tx_buf;
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;
status[7:2] <= '0;
end
if (cs) begin
if (~rw) begin
if (addr == 0)
tx_buf <= data_in;
end else begin
if (addr == 0)
data_out <= rx_buf;
if (addr == 1)
data_out <= status;
end
end
if (~rw & cs && addr == 0)
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
typedef enum bit [2:0] {START, DATA, PARITY, STOP, IDLE} macro_t;
struct packed {
macro_t macro;
logic [3:0] count;
} 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] tx_clkdiv;
always_ff @(posedge clk_50) begin
if (rst) begin
tx_clkdiv <= 0;
tx_state.macro <= IDLE;
tx_state.count <= 3'b0;
tx_flag <= '0;
end else begin
if (tx_flag_set)
tx_flag <= '1;
else if (tx_flag_clear)
tx_flag <= '0;
if (tx_clkdiv == count) begin
tx_clkdiv <= 0;
tx_state <= tx_next_state;
end else begin
tx_clkdiv <= tx_clkdiv + 15'b1;
end
end
end
always_comb begin
tx_next_state = tx_state;
unique case (tx_state.macro)
START: begin
tx_next_state.macro = DATA;
tx_next_state.count = 3'b0;
end
DATA: begin
if (tx_state.count == maxcount) begin
tx_next_state.macro = STOP; // or PARITY
tx_next_state.count = 3'b0;
end else begin
tx_next_state.count = tx_state.count + 3'b1;
tx_next_state.macro = DATA;
end
end
PARITY: begin
end
STOP: begin
tx_next_state.macro = IDLE;
tx_next_state.count = '0;
end
IDLE: begin
if (tx_flag)
tx_next_state.macro = START;
else
tx_next_state.macro = IDLE;
end
default:;
endcase
end
always_comb begin
TXD = '1;
tx_flag_clear = '0;
unique case (tx_state.macro)
START: begin
TXD = '0;
end
DATA: begin
TXD = tx_buf[tx_state.count];
end
PARITY: begin
end
STOP: begin
tx_flag_clear = '1;
TXD = '1;
end
IDLE: begin
TXD = '1;
end
default:;
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