123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547 |
- //-----------------------------------------------------------------
- // 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
|