Skip to content

Instantly share code, notes, and snippets.

@bit-hack
Last active December 9, 2019 00:28
Show Gist options
  • Save bit-hack/0c37e5c8997b2443423c0fe1f44b09ee to your computer and use it in GitHub Desktop.
Save bit-hack/0c37e5c8997b2443423c0fe1f44b09ee to your computer and use it in GitHub Desktop.
RS232 Transceiver
`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