Last active
December 9, 2019 00:28
-
-
Save bit-hack/0c37e5c8997b2443423c0fe1f44b09ee to your computer and use it in GitHub Desktop.
RS232 Transceiver
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
`default_nettype none | |
`timescale 1ns / 1ps | |
module uart_tx( | |
input clk, // Master clock | |
input rst, // Synchronous reset | |
input [7:0] tx_byte, // Byte to transmit | |
input start, // Start | |
output tx // transmit pin | |
); | |
parameter baud_rate = 9600; | |
parameter sys_clk_freq = 48000000; | |
// receive counts | |
localparam full_cycle = sys_clk_freq / baud_rate; | |
localparam half_cycle = full_cycle / 2; | |
// receive clock | |
localparam clk_bits = $clog2(full_cycle); | |
reg [clk_bits-1:0] rx_clk; | |
// state machine | |
localparam [1:0] | |
STATE_IDLE = 1'd0, | |
STATE_SEND = 1'd1; | |
reg state; | |
reg bit_tx; | |
reg [3:0] bit_cnt; | |
reg [9:0] bit_shift; | |
reg old_start; | |
reg pending_start; | |
assign tx = bit_tx; | |
initial begin | |
state <= STATE_IDLE; | |
bit_tx <= 0; | |
bit_shift <= 0; | |
rx_clk <= 0; | |
bit_cnt <= 0; | |
pending_start <= 0; | |
end | |
always @(posedge clk) begin | |
if (rst) begin | |
state <= STATE_IDLE; | |
bit_tx <= 0; | |
bit_shift <= 0; | |
rx_clk <= 0; | |
bit_cnt <= 0; | |
pending_start <= 0; | |
end else begin | |
if (old_start == 1'b0 && start == 1'b1 && pending_start == 0) begin | |
pending_start <= 1'b1; | |
end | |
old_start <= start; | |
case (state) | |
STATE_IDLE: begin | |
bit_cnt <= 0; | |
bit_tx <= pending_start ? 1'b0 : 1'b1; | |
if (pending_start == 1'b1) begin | |
pending_start <= 1'b0; | |
state <= STATE_SEND; | |
rx_clk <= 0; | |
// prepare shift register | |
// start byte end | |
bit_shift <= { 1'b1, tx_byte, 1'b0 }; | |
end | |
end | |
STATE_SEND: begin | |
if (rx_clk == full_cycle) begin | |
if (bit_cnt == 9) begin | |
state <= STATE_IDLE; | |
end else begin | |
rx_clk <= 0; | |
bit_cnt <= bit_cnt + 1; | |
bit_shift <= { 1'b0, bit_shift[9:1] }; | |
end | |
end else begin | |
if (rx_clk == 0) begin | |
// send MSB | |
bit_tx <= bit_shift[0]; | |
end | |
rx_clk <= rx_clk + 1; | |
end | |
end | |
endcase | |
end | |
end | |
endmodule | |
module uart_rx( | |
input clk, // Master clock | |
input rst, // Synchronous reset | |
input rx, // Incoming serial line | |
output received, // Indicates that a byte has been received | |
output [7:0] rx_byte // Byte received | |
); | |
parameter baud_rate = 9600; | |
parameter sys_clk_freq = 48000000; | |
// receive counts | |
localparam full_cycle = sys_clk_freq / baud_rate; | |
localparam half_cycle = full_cycle / 2; | |
localparam quat_cycle = full_cycle / 4; | |
// state machine | |
localparam [1:0] | |
STATE_IDLE = 2'd0, | |
STATE_RECV = 2'd1, | |
STATE_STOP = 2'd2; | |
reg [1:0] state; | |
// receive clock | |
localparam clk_bits = $clog2(full_cycle); | |
reg [clk_bits-1:0] rx_clk; | |
// bytes shifted in | |
reg [3:0] rx_count; | |
// received byte | |
reg [7:0] rx_out; | |
// temporary shift register | |
reg [7:0] rx_shift; | |
reg [clk_bits-1:0] rx_filter; | |
assign rx_byte = rx_out; | |
assign received = (state == STATE_STOP); | |
initial begin | |
state <= STATE_IDLE; | |
rx_clk <= 0; | |
rx_count <= 0; | |
rx_out <= 8'd0; | |
rx_shift <= 8'd0; | |
rx_filter <= 0; | |
end | |
always @(posedge clk) begin | |
if (rst) begin | |
state <= STATE_IDLE; | |
rx_clk <= 0; | |
rx_count <= 0; | |
rx_out <= 8'd0; | |
rx_shift <= 8'd0; | |
rx_filter <= 0; | |
end else begin | |
case (state) | |
// in an idle state | |
// wait for rx to drop low for a half cycle so we can be confident we have | |
// a start bit. wait in idle state for a full cycle before proceeding so | |
// we have a full start bit. | |
STATE_IDLE: begin | |
rx_count <= 0; | |
rx_filter <= 0; | |
if (rx == 0 || rx_clk > half_cycle) begin | |
if (rx_clk == full_cycle) begin | |
state <= STATE_RECV; | |
rx_clk <= 0; | |
end else begin | |
rx_clk <= rx_clk + 1; | |
end | |
end else begin | |
// false start reset | |
// this might be too harsh and could be filtered | |
rx_clk <= 0; | |
end | |
end | |
// receive 8 bits that make up our byte. sample the data on the half cycle | |
// so the signal is stable. we use an average filter to smooth and errors. | |
STATE_RECV: begin | |
if (rx_clk == half_cycle) begin | |
// clock in a new byte | |
// 1 or 0 depending on which it was more often during the half cycle | |
rx_shift <= { ( rx_filter > quat_cycle ? 1'b1 : 1'b0), rx_shift[7:1] }; | |
rx_count <= rx_count + 1; | |
rx_clk <= rx_clk + 1; | |
end else begin | |
if (rx_clk == full_cycle) begin | |
rx_clk <= 0; | |
rx_filter <= 0; | |
if (rx_count == 8) begin | |
rx_out <= rx_shift; | |
state <= STATE_STOP; | |
end | |
end else begin | |
rx_clk <= rx_clk + 1; | |
rx_filter <= rx_filter + rx; | |
end | |
end | |
end | |
// parse the stop bit where rx goes high for one cycle. | |
STATE_STOP: begin | |
if (rx_clk == full_cycle) begin | |
state <= STATE_IDLE; | |
rx_clk <= 0; | |
end else begin | |
rx_clk <= rx_clk + 1; | |
end | |
end | |
// we should never get here | |
default: begin | |
state <= STATE_IDLE; | |
end | |
endcase | |
end | |
end | |
endmodule |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment