| `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 |