diff options
-rw-r--r-- | Makefile | 1 | ||||
-rw-r--r-- | hdl/main_xula2.v | 24 | ||||
-rw-r--r-- | hdl/simple_uart.v | 214 |
3 files changed, 239 insertions, 0 deletions
@@ -27,6 +27,7 @@ iseenv := /opt/Xilinx/14.3/ISE_DS/ # list all .v files explicitly with verilog_files (no hdl/*.v business) verilog_files := hdl/$(top_module)_$(board).v verilog_files += hdl/rot13.v +verilog_files += hdl/simple_uart.v #verilog_files += hdl/yours.v tbfiles := tb/rot13_tb.v diff --git a/hdl/main_xula2.v b/hdl/main_xula2.v index 926f634..2f0d662 100644 --- a/hdl/main_xula2.v +++ b/hdl/main_xula2.v @@ -32,6 +32,30 @@ module main ( reg throb_led = 0; assign chan[10] = throb_led; + wire [7:0] rx_byte; + wire [7:0] tx_byte; + wire uart_flag; + simple_uart simple_uart_inst ( + .clk(clock_12mhz), + .rst(reset), + .rx(chan[17]), + .tx(chan[18]), + .transmit(uart_flag), + .tx_byte(tx_byte), + .received(uart_flag), + .rx_byte(rx_byte), + .is_receiving(), + .is_transmitting(), + .recv_error() + ); + + rot13 rot13_inst ( + .clock(clock_12mhz), + .reset(reset), + .in_char(rx_byte), + .out_char(tx_byte) + ); + always @(posedge clock_12mhz) begin if (reset) begin throb_counter <= 0; diff --git a/hdl/simple_uart.v b/hdl/simple_uart.v new file mode 100644 index 0000000..b5848ae --- /dev/null +++ b/hdl/simple_uart.v @@ -0,0 +1,214 @@ +`timescale 1ns / 1ps +// Documented Verilog UART +// Copyright (C) 2010 Timothy Goddard (tim@goddard.net.nz) +// Distributed under the MIT licence. +// +// 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. +// +module simple_uart ( + input clk, // The master clock for this module + input rst, // Synchronous reset. + input rx, // Incoming serial line + output tx, // Outgoing serial line + input transmit, // Signal to transmit + input [7:0] tx_byte, // Byte to transmit + output received, // Indicated that a byte has been received. + output [7:0] rx_byte, // Byte received + output is_receiving, // Low when receive line is idle. + output is_transmitting, // Low when transmit line is idle. + output recv_error // Indicates error in receiving packet. + ); + + //parameter CLOCK_DIVIDE = 3255; // clock rate (125Mhz) / (baud rate (9600) * 4) + parameter CLOCK_DIVIDE = 313; // clock rate (25Mhz) / (baud rate (9600) * 4) + + // States for the receiving state machine. + // These are just constants, not parameters to override. + localparam RX_IDLE = 3'd0; + localparam RX_CHECK_START = 3'd1; + localparam RX_READ_BITS = 3'd2; + localparam RX_CHECK_STOP = 3'd3; + localparam RX_DELAY_RESTART = 3'd4; + localparam RX_ERROR = 3'd5; + localparam RX_RECEIVED = 3'd6; + + // States for the transmitting state machine. + // Constants - do not override. + localparam TX_IDLE = 2'd0; + localparam TX_SENDING = 2'd1; + localparam TX_DELAY_RESTART = 2'd2; + + reg [10:0] rx_clk_divider = CLOCK_DIVIDE; + reg [10:0] tx_clk_divider = CLOCK_DIVIDE; + + reg [2:0] recv_state = RX_IDLE; + reg [5:0] rx_countdown; + reg [3:0] rx_bits_remaining; + reg [7:0] rx_data = 0; + + reg tx_out = 1'b1; + reg [1:0] tx_state = TX_IDLE; + reg [5:0] tx_countdown; + reg [3:0] tx_bits_remaining; + reg [7:0] tx_data; + + assign received = recv_state == RX_RECEIVED; + assign recv_error = recv_state == RX_ERROR; + assign is_receiving = recv_state != RX_IDLE; + assign rx_byte = rx_data; + + assign tx = tx_out; + assign is_transmitting = tx_state != TX_IDLE; + + always @(posedge clk) begin + if (rst) begin + recv_state = RX_IDLE; + tx_state = TX_IDLE; + end + + // The clk_divider counter counts down from + // the CLOCK_DIVIDE constant. Whenever it + // reaches 0, 1/16 of the bit period has elapsed. + // Countdown timers for the receiving and transmitting + // state machines are decremented. + rx_clk_divider = rx_clk_divider - 11'd1; + if (!rx_clk_divider) begin + rx_clk_divider = CLOCK_DIVIDE; + rx_countdown = rx_countdown - 6'd1; + end + tx_clk_divider = tx_clk_divider - 11'd1; + if (!tx_clk_divider) begin + tx_clk_divider = CLOCK_DIVIDE; + tx_countdown = tx_countdown - 6'd1; + end + + // Receive state machine + case (recv_state) + RX_IDLE: begin + // A low pulse on the receive line indicates the + // start of data. + if (!rx) begin + // Wait half the period - should resume in the + // middle of this first pulse. + rx_clk_divider = CLOCK_DIVIDE; + rx_countdown = 2; + recv_state = RX_CHECK_START; + end + end + RX_CHECK_START: begin + if (!rx_countdown) begin + // Check the pulse is still there + if (!rx) begin + // Pulse still there - good + // Wait the bit period to resume half-way + // through the first bit. + rx_countdown = 4; + rx_bits_remaining = 8; + recv_state = RX_READ_BITS; + end else begin + // Pulse lasted less than half the period - + // not a valid transmission. + recv_state = RX_ERROR; + end + end + end + RX_READ_BITS: begin + if (!rx_countdown) begin + // Should be half-way through a bit pulse here. + // Read this bit in, wait for the next if we + // have more to get. + rx_data = {rx, rx_data[7:1]}; + rx_countdown = 4; + rx_bits_remaining = rx_bits_remaining - 4'd1; + recv_state = rx_bits_remaining ? RX_READ_BITS : RX_CHECK_STOP; + end + end + RX_CHECK_STOP: begin + if (!rx_countdown) begin + // Should resume half-way through the stop bit + // This should be high - if not, reject the + // transmission and signal an error. + recv_state = rx ? RX_RECEIVED : RX_ERROR; + end + end + RX_DELAY_RESTART: begin + // Waits a set number of cycles before accepting + // another transmission. + recv_state = rx_countdown ? RX_DELAY_RESTART : RX_IDLE; + end + RX_ERROR: begin + // There was an error receiving. + // Raises the recv_error flag for one clock + // cycle while in this state and then waits + // 2 bit periods before accepting another + // transmission. + rx_countdown = 8; + recv_state = RX_DELAY_RESTART; + end + RX_RECEIVED: begin + // Successfully received a byte. + // Raises the received flag for one clock + // cycle while in this state. + recv_state = RX_IDLE; + end + endcase + + // Transmit state machine + case (tx_state) + TX_IDLE: begin + if (transmit) begin + // If the transmit flag is raised in the idle + // state, start transmitting the current content + // of the tx_byte input. + tx_data = tx_byte; + // Send the initial, low pulse of 1 bit period + // to signal the start, followed by the data + tx_clk_divider = CLOCK_DIVIDE; + tx_countdown = 4; + tx_out = 0; + tx_bits_remaining = 8; + tx_state = TX_SENDING; + end + end + TX_SENDING: begin + if (!tx_countdown) begin + if (tx_bits_remaining) begin + tx_bits_remaining = tx_bits_remaining - 4'd1; + tx_out = tx_data[0]; + tx_data = {1'b0, tx_data[7:1]}; + tx_countdown = 4; + tx_state = TX_SENDING; + end else begin + // Set delay to send out 2 stop bits. + tx_out = 1; + tx_countdown = 8; + tx_state = TX_DELAY_RESTART; + end + end + end + TX_DELAY_RESTART: begin + // Wait until tx_countdown reaches the end before + // we send another transmission. This covers the + // "stop bit" delay. + tx_state = tx_countdown ? TX_DELAY_RESTART : TX_IDLE; + end + endcase + end + +endmodule |