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.
229 lines
5.1 KiB
Systemverilog
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
|