//----------------------------------------------------------------- // USB Serial Port // V0.1 // Ultra-Embedded.com // Copyright 2020 // // Email: admin@ultra-embedded.com // // License: LGPL //----------------------------------------------------------------- // // This source file may be used and distributed without // restriction provided that this copyright statement is not // removed from the file and that any derivative work contains // the original copyright notice and the associated disclaimer. // // This source file is free software; you can redistribute it // and/or modify it under the terms of the GNU Lesser General // Public License as published by the Free Software Foundation; // either version 2.1 of the License, or (at your option) any // later version. // // This source is distributed in the hope that it will be // useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the GNU Lesser General Public License for more // details. // // You should have received a copy of the GNU Lesser General // Public License along with this source; if not, write to the // Free Software Foundation, Inc., 59 Temple Place, Suite 330, // Boston, MA 02111-1307 USA //----------------------------------------------------------------- //----------------------------------------------------------------- // Generated File //----------------------------------------------------------------- module usb_cdc_top //----------------------------------------------------------------- // Params //----------------------------------------------------------------- #( parameter BAUDRATE = 115200 ) //----------------------------------------------------------------- // Ports //----------------------------------------------------------------- ( input clk_i, input rst_i, output [7:0] utmi_data_out_o, output utmi_txvalid_o, output [1:0] utmi_op_mode_o, output [1:0] utmi_xcvrselect_o, output utmi_termselect_o, output utmi_dppulldown_o, output utmi_dmpulldown_o, input [7:0] utmi_data_in_i, input utmi_txready_i, input utmi_rxvalid_i, input utmi_rxactive_i, input utmi_rxerror_i, input [1:0] utmi_linestate_i, input sys_clk, input cpu_valid, input [15:0] cpu_addr, output [31:0] cpu_rdata, input [31:0] cpu_wdata, input [3:0] cpu_wstrb, input tx_i, output rx_o, output rx_break_o ); wire [ 7:0] usb_rx_data_w; wire usb_tx_accept_w; wire enable_w = 1'h1; wire usb_tx_valid_w; wire usb_rx_accept_w; wire [ 7:0] usb_tx_data_w; wire usb_rx_valid_w; wire usb_rx_break_w; usb_cdc_core u_usb ( // Inputs .clk_i(clk_i) ,.rst_i(rst_i) ,.enable_i(enable_w) ,.utmi_data_in_i(utmi_data_in_i) ,.utmi_txready_i(utmi_txready_i) ,.utmi_rxvalid_i(utmi_rxvalid_i) ,.utmi_rxactive_i(utmi_rxactive_i) ,.utmi_rxerror_i(utmi_rxerror_i) ,.utmi_linestate_i(utmi_linestate_i) ,.inport_valid_i(usb_tx_valid_w) ,.inport_data_i(usb_tx_data_w) ,.outport_accept_i(usb_rx_accept_w) // Outputs ,.utmi_data_out_o(utmi_data_out_o) ,.utmi_txvalid_o(utmi_txvalid_o) ,.utmi_op_mode_o(utmi_op_mode_o) ,.utmi_xcvrselect_o(utmi_xcvrselect_o) ,.utmi_termselect_o(utmi_termselect_o) ,.utmi_dppulldown_o(utmi_dppulldown_o) ,.utmi_dmpulldown_o(utmi_dmpulldown_o) ,.inport_accept_o(usb_tx_accept_w) ,.outport_valid_o(usb_rx_valid_w) ,.outport_data_o(usb_rx_data_w) ,.outport_break_o(usb_rx_break_w), // CPU bus .sys_clk ( sys_clk ), .cpu_valid ( cpu_valid ), .cpu_addr ( cpu_addr ), .cpu_rdata ( cpu_rdata ), .cpu_wdata ( cpu_wdata ), .cpu_wstrb ( cpu_wstrb ) ); //----------------------------------------------------------------- // Output FIFO //----------------------------------------------------------------- wire tx_valid_w; wire [7:0] tx_data_w; wire tx_accept_w; usb_cdc_fifo #( .WIDTH(8), .DEPTH(512), .ADDR_W(9) ) u_fifo_tx ( .clk_i(clk_i), .rst_i(rst_i), // In .push_i(tx_valid_w), .data_in_i(tx_data_w), .accept_o(tx_accept_w), // Out .pop_i(usb_tx_accept_w), .data_out_o(usb_tx_data_w), .valid_o(usb_tx_valid_w) ); //----------------------------------------------------------------- // Input FIFO //----------------------------------------------------------------- wire rx_valid_w; wire [7:0] rx_data_w; wire rx_accept_w; usb_cdc_fifo #( .WIDTH(8), .DEPTH(512), .ADDR_W(9) ) u_fifo_rx ( .clk_i(clk_i), .rst_i(rst_i), // In .push_i(usb_rx_valid_w), .data_in_i(usb_rx_data_w), .accept_o(usb_rx_accept_w), // Out .pop_i(rx_accept_w), .data_out_o(rx_data_w), .valid_o(rx_valid_w) ); //----------------------------------------------------------------- // Registers //----------------------------------------------------------------- // Configuration localparam STOP_BITS = 1'b0; // 0 = 1, 1 = 2 localparam CLK_FREQ = 48000000; localparam BIT_DIV = (CLK_FREQ / BAUDRATE) - 1; localparam START_BIT = 4'd0; localparam STOP_BIT0 = 4'd9; localparam STOP_BIT1 = 4'd10; // TX Signals reg tx_busy_q; reg [3:0] tx_bits_q; reg [31:0] tx_count_q; reg [7:0] tx_shift_reg_q; reg txd_q; // RX Signals reg rxd_q; reg [7:0] rx_data_q; reg [3:0] rx_bits_q; reg [31:0] rx_count_q; reg [7:0] rx_shift_reg_q; reg rx_ready_q; reg rx_busy_q; reg rx_err_q; //----------------------------------------------------------------- // Re-sync RXD //----------------------------------------------------------------- reg rxd_ms_q; always @ (posedge clk_i or posedge rst_i) if (rst_i) begin rxd_ms_q <= 1'b1; rxd_q <= 1'b1; end else begin rxd_ms_q <= tx_i; rxd_q <= rxd_ms_q; end //----------------------------------------------------------------- // RX Clock Divider //----------------------------------------------------------------- wire rx_sample_w = (rx_count_q == 32'b0); always @ (posedge clk_i or posedge rst_i) if (rst_i) rx_count_q <= 32'b0; else begin // Inactive if (!rx_busy_q) rx_count_q <= {1'b0, BIT_DIV[31:1]}; // Rx bit timer else if (rx_count_q != 0) rx_count_q <= (rx_count_q - 1); // Active else if (rx_sample_w) begin // Last bit? if ((rx_bits_q == STOP_BIT0 && !STOP_BITS) || (rx_bits_q == STOP_BIT1 && STOP_BITS)) rx_count_q <= 32'b0; else rx_count_q <= BIT_DIV; end end //----------------------------------------------------------------- // RX Shift Register //----------------------------------------------------------------- always @ (posedge clk_i or posedge rst_i) if (rst_i) begin rx_shift_reg_q <= 8'h00; rx_busy_q <= 1'b0; end // Rx busy else if (rx_busy_q && rx_sample_w) begin // Last bit? if (rx_bits_q == STOP_BIT0 && !STOP_BITS) rx_busy_q <= 1'b0; else if (rx_bits_q == STOP_BIT1 && STOP_BITS) rx_busy_q <= 1'b0; else if (rx_bits_q == START_BIT) begin // Start bit should still be low as sampling mid // way through start bit, so if high, error! if (rxd_q) rx_busy_q <= 1'b0; end // Rx shift register else rx_shift_reg_q <= {rxd_q, rx_shift_reg_q[7:1]}; end // Start bit? else if (!rx_busy_q && rxd_q == 1'b0) begin rx_shift_reg_q <= 8'h00; rx_busy_q <= 1'b1; end always @ (posedge clk_i or posedge rst_i) if (rst_i) rx_bits_q <= START_BIT; else if (rx_sample_w && rx_busy_q) begin if ((rx_bits_q == STOP_BIT1 && STOP_BITS) || (rx_bits_q == STOP_BIT0 && !STOP_BITS)) rx_bits_q <= START_BIT; else rx_bits_q <= rx_bits_q + 4'd1; end else if (!rx_busy_q && (BIT_DIV == 32'b0)) rx_bits_q <= START_BIT + 4'd1; else if (!rx_busy_q) rx_bits_q <= START_BIT; //----------------------------------------------------------------- // RX Data //----------------------------------------------------------------- always @ (posedge clk_i or posedge rst_i) if (rst_i) begin rx_ready_q <= 1'b0; rx_data_q <= 8'h00; rx_err_q <= 1'b0; end else begin // If reading data, reset data state if (tx_accept_w) begin rx_ready_q <= 1'b0; rx_err_q <= 1'b0; end if (rx_busy_q && rx_sample_w) begin // Stop bit if ((rx_bits_q == STOP_BIT1 && STOP_BITS) || (rx_bits_q == STOP_BIT0 && !STOP_BITS)) begin // RXD should be still high if (rxd_q) begin rx_data_q <= rx_shift_reg_q; rx_ready_q <= 1'b1; end // Bad Stop bit - wait for a full bit period // before allowing start bit detection again else begin rx_ready_q <= 1'b0; rx_data_q <= 8'h00; rx_err_q <= 1'b1; end end // Mid start bit sample - if high then error else if (rx_bits_q == START_BIT && rxd_q) rx_err_q <= 1'b1; end end assign tx_data_w = rx_data_q; assign tx_valid_w = rx_ready_q; //----------------------------------------------------------------- // TX Clock Divider //----------------------------------------------------------------- wire tx_sample_w = (tx_count_q == 32'b0); always @ (posedge clk_i or posedge rst_i) if (rst_i) tx_count_q <= 32'b0; else begin // Idle if (!tx_busy_q) tx_count_q <= BIT_DIV; // Tx bit timer else if (tx_count_q != 0) tx_count_q <= (tx_count_q - 1); else if (tx_sample_w) tx_count_q <= BIT_DIV; end //----------------------------------------------------------------- // TX Shift Register //----------------------------------------------------------------- reg tx_complete_q; always @ (posedge clk_i or posedge rst_i) if (rst_i) begin tx_shift_reg_q <= 8'h00; tx_busy_q <= 1'b0; tx_complete_q <= 1'b0; end // Tx busy else if (tx_busy_q) begin // Shift tx data if (tx_bits_q != START_BIT && tx_sample_w) tx_shift_reg_q <= {1'b0, tx_shift_reg_q[7:1]}; // Last bit? if (tx_bits_q == STOP_BIT0 && tx_sample_w && !STOP_BITS) begin tx_busy_q <= 1'b0; tx_complete_q <= 1'b1; end else if (tx_bits_q == STOP_BIT1 && tx_sample_w && STOP_BITS) begin tx_busy_q <= 1'b0; tx_complete_q <= 1'b1; end end // Buffer data to transmit else if (rx_valid_w) begin tx_shift_reg_q <= rx_data_w; tx_busy_q <= 1'b1; tx_complete_q <= 1'b0; end else tx_complete_q <= 1'b0; assign rx_accept_w = ~tx_busy_q; always @ (posedge clk_i or posedge rst_i) if (rst_i) tx_bits_q <= 4'd0; else if (tx_sample_w && tx_busy_q) begin if ((tx_bits_q == STOP_BIT1 && STOP_BITS) || (tx_bits_q == STOP_BIT0 && !STOP_BITS)) tx_bits_q <= START_BIT; else tx_bits_q <= tx_bits_q + 4'd1; end //----------------------------------------------------------------- // UART Tx Pin //----------------------------------------------------------------- reg txd_r; always @ * begin txd_r = 1'b1; if (tx_busy_q) begin // Start bit (TXD = L) if (tx_bits_q == START_BIT) txd_r = 1'b0; // Stop bits (TXD = H) else if (tx_bits_q == STOP_BIT0 || tx_bits_q == STOP_BIT1) txd_r = 1'b1; // Data bits else txd_r = tx_shift_reg_q[0]; end end always @ (posedge clk_i or posedge rst_i) if (rst_i) txd_q <= 1'b1; else txd_q <= txd_r; assign rx_o = txd_q; assign rx_break_o = usb_rx_break_w; endmodule module usb_cdc_fifo //----------------------------------------------------------------- // Params //----------------------------------------------------------------- #( parameter WIDTH = 8, parameter DEPTH = 4, parameter ADDR_W = 2 ) //----------------------------------------------------------------- // Ports //----------------------------------------------------------------- ( // Inputs input clk_i ,input rst_i ,input [WIDTH-1:0] data_in_i ,input push_i ,input pop_i // Outputs ,output [WIDTH-1:0] data_out_o ,output accept_o ,output valid_o ); //----------------------------------------------------------------- // Local Params //----------------------------------------------------------------- localparam COUNT_W = ADDR_W + 1; //----------------------------------------------------------------- // Registers //----------------------------------------------------------------- reg [WIDTH-1:0] ram_q[DEPTH-1:0]; reg [ADDR_W-1:0] rd_ptr_q; reg [ADDR_W-1:0] wr_ptr_q; reg [COUNT_W-1:0] count_q; //----------------------------------------------------------------- // Sequential //----------------------------------------------------------------- always @ (posedge clk_i ) if (rst_i) begin count_q <= {(COUNT_W) {1'b0}}; rd_ptr_q <= {(ADDR_W) {1'b0}}; wr_ptr_q <= {(ADDR_W) {1'b0}}; end else begin // Push if (push_i & accept_o) begin ram_q[wr_ptr_q] <= data_in_i; wr_ptr_q <= wr_ptr_q + 1; end // Pop if (pop_i & valid_o) rd_ptr_q <= rd_ptr_q + 1; // Count up if ((push_i & accept_o) & ~(pop_i & valid_o)) count_q <= count_q + 1; // Count down else if (~(push_i & accept_o) & (pop_i & valid_o)) count_q <= count_q - 1; end //------------------------------------------------------------------- // Combinatorial //------------------------------------------------------------------- /* verilator lint_off WIDTH */ assign valid_o = (count_q != 0); assign accept_o = (count_q != DEPTH); /* verilator lint_on WIDTH */ assign data_out_o = ram_q[rd_ptr_q]; endmodule