blob: 9cc276210ee2e9268f5fc9e9196312e8e3e779df [file] [log] [blame]
module uart (
input i_clk,
input i_full_clk,
input i_rst,
input rx,
output reg tx,
input wb_cyc, wb_stb, wb_we,
output wb_ack,
input [23:0] wb_adr,
input [15:0] wb_i_dat,
output reg [15:0] wb_o_dat
);
localparam BAUD_RATE = 115200;
localparam OVERSAMPLE = 8;
localparam OVERSAMPLE_LOG = 3;
localparam CLOCK_FREQ = 50_000_000;
localparam UART_CLOCK_DIV = CLOCK_FREQ/(BAUD_RATE*2);
localparam OSMPL_CLOCK_DIV = CLOCK_FREQ/(BAUD_RATE*OVERSAMPLE*2);
localparam RX_BUFF_SIZE = 8;
localparam TX_BUFF_SIZE = 8;
reg uart_os_clk = 1'b0;
reg [5:0] uart_os_clk_cnt = 6'b0;
reg uart_clk = 1'b0;
reg [9:0] uart_clk_cnt = 10'b0;
always @(posedge i_full_clk) begin
uart_os_clk_cnt <= uart_os_clk_cnt + 6'b1;
uart_clk_cnt <= uart_clk_cnt + 6'b1;
if (uart_os_clk_cnt == 27) begin
uart_os_clk_cnt <= 6'b0;
uart_os_clk <= ~uart_os_clk;
end
if (uart_clk_cnt == UART_CLOCK_DIV) begin
uart_clk_cnt <= 10'b0;
uart_clk <= ~uart_clk;
end
end
// RECEIVE
// reg rx_active = 1'b0, rx_stop = 1'b0;
// reg [OVERSAMPLE_LOG:0] rx_os_cnt;
reg [7:0] rx_result;
reg [2:0] rx_res_bit;
//wire rx_submit = (rx_stop & (rx_os_cnt == 0));
wire rx_submit = (sub_clk_cnt == 4'b0111) && (state == 4'b1001);
//assign dbg_trig = rx_submit & ~rx;
// I DONT KNOW WHY but this code from old cpu works just fine,
// but preetier new code that seems to behave in the same way misses the stop bits. WHY???
reg [3:0] state = 4'b0;
reg [3:0] sub_clk_cnt = 4'b0;
//reg r1_trig = 1'b0;
always @(posedge uart_os_clk) begin
case (state)
4'b0: begin
// if start bit
if(rx == 1'b0) begin
state <= 1'b1;
end
end
4'b1001: begin
if(sub_clk_cnt == 4'b0111) begin
sub_clk_cnt <= 4'b0;
state <= 4'b0;
//r1_trig <= ~r1_trig;
end else begin
sub_clk_cnt <= sub_clk_cnt+4'b1;
end
end
default: begin // default read bit
if((state != 4'b1 && sub_clk_cnt == 4'b0111) || sub_clk_cnt == 4'b1010) begin //delay first clock by one and half
rx_result[state-4'b1] <= rx;
state <= state+4'b1;
sub_clk_cnt <= 4'b0;
//r1_trig <= ~r1_trig;
end else begin
sub_clk_cnt <= sub_clk_cnt+4'b1;
end
end
endcase
end
// reg r2_trig = 1'b0;
// always @(posedge uart_os_clk) begin
// if (i_rst) begin
// rx_active <= 1'b0;
// rx_os_cnt <= 'b0;
// rx_stop <= 1'b0;
// end else begin
// if (~rx_active & ~rx) begin
// rx_active <= 1'b1;
// rx_res_bit <= 'b0;
// rx_os_cnt <= 4'b1010;
// end else if (rx_stop & (rx_os_cnt == 0)) begin
// rx_active <= 1'b0;
// rx_stop <= 1'b0;
// r2_trig <= ~r2_trig;
// //dbg_trig <= ~dbg_trig;
// end else if (rx_active & ~rx_stop & (rx_os_cnt == 0)) begin
// rx_os_cnt <= 4'b0111;
// r2_trig <= ~r2_trig;
// //rx_result[rx_res_bit] <= rx;
// rx_res_bit <= rx_res_bit + 1'b1;
// rx_stop <= (rx_res_bit == 3'b111);
// //dbg_trig <= ~dbg_trig;
// end else if (rx_active) begin
// rx_os_cnt <= rx_os_cnt - 1'b1;
// end
// end
// end
reg [7:0] rx_fifo [RX_BUFF_SIZE-1:0];
reg [2:0] rx_write_ptr, rx_read_ptr;
always @(posedge uart_os_clk) begin
if (i_rst) begin
rx_write_ptr <= 3'b0;
end else if (rx_submit) begin
rx_fifo[rx_write_ptr] <= rx_result;
rx_write_ptr <= rx_write_ptr + 3'b1;
end
end
wire rx_data_available = |(rx_read_ptr ^ rx_write_ptr);
reg [2:0] rx_prev_data;
wire rx_irq = (rx_prev_data == 3'b0 & ((rx_write_ptr-rx_read_ptr) != 3'b0));
always @(posedge i_clk) begin
if (i_rst) begin
rx_read_ptr <= 3'b0;
end else begin
rx_prev_data <= rx_write_ptr-rx_read_ptr;
if (wb_cyc & wb_stb & ~wb_we & wb_adr == 24'h1 & rx_data_available) begin
rx_read_ptr <= rx_read_ptr + 3'b1;
rx_prev_data <= (rx_write_ptr-rx_read_ptr-3'b1);
end
end
end
// TRANSMIT
reg [1:0] tx_state;
wire tx_ready = (tx_state == 2'b0);
wire tx_start = tx_data_avail;
reg [7:0] tx_data;
reg [2:0] tx_data_cnt;
always @(posedge uart_clk) begin
if (i_rst) begin
tx <= 1'b1;
tx_state <= 2'b0;
end else if ((tx_state == 2'b0) & tx_start) begin
tx <= 1'b0; // start bit
tx_data_cnt <= 3'b0;
tx_state <= 2'b1;
end else if (tx_state == 2'b1) begin
tx <= tx_data[tx_data_cnt];
tx_data_cnt <= tx_data_cnt + 1'b1;
if (&tx_data_cnt)
tx_state <= 2'b10;
end else if (tx_state == 2'b10) begin
tx <= 1'b1; // stop bit
tx_state <= 2'b0;
end
end
reg [7:0] tx_fifo [RX_BUFF_SIZE-1:0];
reg [2:0] tx_write_ptr, tx_read_ptr;
wire tx_data_avail = |(tx_write_ptr^tx_read_ptr);
wire tx_full = (tx_write_ptr+3'b1) == tx_read_ptr;
always @(posedge uart_clk) begin
if (i_rst) begin
tx_read_ptr <= 3'b0;
end else if (tx_ready & tx_data_avail) begin
tx_read_ptr <= tx_read_ptr + 3'b1;
tx_data <= tx_fifo[tx_read_ptr];
end
end
reg [2:0] tx_prev_data;
wire tx_empty_irq = (tx_prev_data != 3'b0) && ((tx_write_ptr-tx_read_ptr) == 3'b0);
always @(posedge i_clk) begin
if (i_rst) begin
tx_write_ptr <= 3'b0;
end else begin
tx_prev_data <= (tx_write_ptr-tx_read_ptr);
if (wb_cyc & wb_stb & wb_we & wb_adr == 24'h2) begin
tx_write_ptr <= tx_write_ptr + 3'b1;
tx_fifo[tx_write_ptr] <= wb_i_dat[7:0];
end
end
end
assign wb_ack = wb_cyc & wb_stb;
always @* begin
if (wb_adr == 24'h0) begin
wb_o_dat = {14'b0, ~tx_full, rx_data_available};
end else if (wb_adr == 24'h1) begin
wb_o_dat = rx_fifo[rx_read_ptr];
end else begin
wb_o_dat = 16'b0;
end
end
endmodule