blob: 4d3277b11064d9c03c34c8ec19418d262d4ec744 [file] [log] [blame]
`ifndef _uart_v_
`define _uart_v_
/*
* This module is designed a 3 Mbaud serial port.
* This is the highest data rate supported by
* the popular FT232 USB-to-serial chip.
*
* Copyright (C) 2009 Micah Dowty
* (C) 2018 Trammell Hudson
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
`include "util.v"
module uart_tx(
input clk,
input reset,
output serial,
output reg ready,
input [7:0] data,
input data_strobe
);
parameter DIVISOR = 100;
wire baud_x1;
divide_by_n #(.N(DIVISOR)) baud_x1_div(clk, reset, baud_x1);
reg [7+1+1:0] shiftreg;
reg serial_r;
assign serial = !serial_r;
always @(posedge clk)
if (reset) begin
shiftreg <= 0;
serial_r <= 0;
end
else if (data_strobe) begin
shiftreg <= {
1'b1, // stop bit
data,
1'b0 // start bit (inverted)
};
ready <= 0;
end
else if (baud_x1) begin
if (shiftreg == 0)
begin
/* Idle state is idle high, serial_r is inverted */
serial_r <= 0;
ready <= 1;
end else
serial_r <= !shiftreg[0];
// shift the output register down
shiftreg <= {1'b0, shiftreg[7+1+1:1]};
end else
ready <= (shiftreg == 0);
endmodule
module uart_rx(
input clk,
input reset,
input serial,
output [7:0] data,
output data_strobe
);
parameter DIVISOR = 25; // should the 1/4 the uart_tx divisor
wire baud_x4;
divide_by_n #(.N(DIVISOR)) baud_x4_div(clk, reset, baud_x4);
// Clock crossing into clk domain
reg [1:0] serial_buf;
wire serial_sync = serial_buf[1];
always @(posedge clk)
serial_buf <= { serial_buf[0], serial };
/*
* State machine: Four clocks per bit, 10 total bits.
*/
reg [8:0] shiftreg;
reg [5:0] state;
reg data_strobe;
wire [3:0] bit_count = state[5:2];
wire [1:0] bit_phase = state[1:0];
wire sampling_phase = (bit_phase == 1);
wire start_bit = (bit_count == 0 && sampling_phase);
wire stop_bit = (bit_count == 9 && sampling_phase);
wire waiting_for_start = (state == 0 && serial_sync == 1);
wire error = ( (start_bit && serial_sync == 1) ||
(stop_bit && serial_sync == 0) );
assign data = shiftreg[7:0];
always @(posedge clk or posedge reset)
if (reset) begin
state <= 0;
data_strobe <= 0;
end
else if (baud_x4) begin
if (waiting_for_start || error || stop_bit)
state <= 0;
else
state <= state + 1;
if (bit_phase == 1)
shiftreg <= { serial_sync, shiftreg[8:1] };
data_strobe <= stop_bit && !error;
end
else begin
data_strobe <= 0;
end
endmodule
module uart(
input clk,
input reset,
// physical interface
input serial_rxd,
output serial_txd,
// logical interface
output [7:0] rxd,
output rxd_strobe,
input [7:0] txd,
input txd_strobe,
output txd_ready
);
// todo: rx/tx could share a single clock
parameter DIVISOR = 40; // must be divisible by 4 for rx clock
uart_rx #(.DIVISOR(DIVISOR/4)) rx(
.clk(clk),
.reset(reset),
.serial(serial_rxd),
.data_strobe(rxd_strobe),
.data(rxd),
);
uart_tx #(.DIVISOR(DIVISOR)) tx(
.clk(clk),
.reset(reset),
.serial(serial_txd),
.data(txd),
.data_strobe(txd_strobe),
.ready(txd_ready),
);
endmodule
`endif