/* * ut_pkt_stream.v * * USB Packet Receiver - Stream generator * * vim: ts=4 sw=4 * * Copyright (C) 2022 Sylvain Munaut * SPDX-License-Identifier: CERN-OHL-P-2.0 */ `default_nettype none module ut_pkt_stream ( // RAW PHY state input wire phy_rx_dp, input wire phy_rx_dn, // Packet interface input wire pkt_start, input wire pkt_done_ok, input wire pkt_done_err, input wire [3:0] pkt_pid, input wire pkt_is_sof, input wire pkt_is_token, input wire pkt_is_data, input wire pkt_is_handshake, input wire [10:0] pkt_frameno, input wire [6:0] pkt_addr, input wire [3:0] pkt_endp, input wire [7:0] pkt_data, input wire pkt_data_stb, // Data stream output reg [7:0] stream_data, output reg stream_stb, // Control input wire ctrl_active, // Clock / Reset input wire clk, input wire rst ); `include "usb_defs.vh" // Signals // ------- // Ingress logic // FSM localparam [1:0] IST_IDLE = 0, IST_NONDATA = 1, IST_DATA = 2; reg [1:0] ig_state; reg [1:0] ig_state_nxt; // Timestamps reg [15:0] ig_ts_cur; reg [15:0] ig_ts_sav; reg ig_ts_warn; // Length reg [10:0] ig_len; // Shifter reg [31:0] ig_lh_data; reg ig_lh_now; wire [31:0] ig_lb_data; wire ig_lb_now; reg [31:0] ig_shift_data; reg [3:0] ig_shift_valid; // Data FIFO wire [7:0] df_wdata; wire df_we; wire df_full; wire [7:0] df_rdata; wire df_re; wire df_empty; // Length FIFO wire [11:0] lf_wdata; wire lf_we; wire lf_full; wire [11:0] lf_rdata; wire lf_re; wire lf_empty; // Egress logic // FSM localparam [2:0] EST_IDLE = 0, EST_WAIT_LEN = 1, EST_HDR_0 = 4, EST_HDR_1 = 5, EST_HDR_2 = 6, EST_HDR_3 = 7, EST_DATA_OUT = 2, EST_DATA_PAUSE = 3; reg [2:0] eg_state; reg [2:0] eg_state_nxt; // Length reg [11:0] eg_len; wire eg_len_last; // Data or not ? reg eg_pid_data; // Ingress logic // ------------- // State register always @(posedge clk) if (rst) ig_state <= IST_IDLE; else ig_state <= ig_state_nxt; // Next-State and shifter load control always @(*) begin // Default ig_state_nxt = ig_state; ig_lh_data = 32'hxxxxxxxx; ig_lh_now = 1'b0; // Events case (ig_state) IST_IDLE: begin if (pkt_start) begin if (pkt_is_data) begin ig_state_nxt = IST_DATA; ig_lh_now = 1'b1; ig_lh_data = { pkt_pid, 1'bx, 11'dx, ig_ts_cur }; end else begin ig_state_nxt = IST_NONDATA; end end else if (pkt_done_err) begin ig_lh_now = 1'b1; ig_lh_data = { 4'h0, 1'b0, 11'd0, ig_ts_cur }; end else if (ig_ts_warn) begin ig_lh_now = 1'b1; ig_lh_data = { 4'h0, 1'b1, 9'd0, phy_rx_dp, phy_rx_dn, ig_ts_cur }; end end IST_NONDATA: begin if (pkt_done_ok) begin ig_state_nxt = IST_IDLE; ig_lh_now = 1'b1; ig_lh_data = { pkt_pid, 1'b1, pkt_frameno, ig_ts_sav }; end else if (pkt_done_err) begin ig_state_nxt = IST_IDLE; ig_lh_now = 1'b1; ig_lh_data = { pkt_pid, 1'b0, pkt_frameno, ig_ts_sav }; end end IST_DATA: begin if (pkt_done_ok) begin ig_state_nxt = IST_IDLE; end else if (pkt_done_err) begin ig_state_nxt = IST_IDLE; end end endcase end // Timestamps always @(posedge clk) if (~ctrl_active) ig_ts_cur <= 0; else if ((ig_state == IST_IDLE) && (pkt_start || pkt_done_err || ig_ts_warn)) ig_ts_cur <= 0; else ig_ts_cur <= ig_ts_cur + 1; always @(posedge clk) if (ig_state == IST_IDLE) ig_ts_sav <= ig_ts_cur; always @(posedge clk) ig_ts_warn <= ~ig_ts_warn & &ig_ts_cur[15:4]; // Push data assign ig_lb_now = pkt_data_stb & (ig_state == IST_DATA); assign ig_lb_data = { pkt_data, 24'hxxxxx }; // Data length counter always @(posedge clk) if (ig_state == IST_IDLE) ig_len <= 0; else ig_len <= ig_len + pkt_data_stb; // Feed length+status FIFO assign lf_wdata = { pkt_done_ok, ig_len }; assign lf_we = (pkt_done_ok | pkt_done_err) & (ig_state == IST_DATA); // Shifter always @(posedge clk) begin // Header if (ig_lh_now) begin ig_shift_data <= ig_lh_data; ig_shift_valid <= 4'hf; end // "Data" byte else if (ig_lb_now) begin ig_shift_data <= ig_lb_data; ig_shift_valid <= 4'h8; end // Nothing just shift out else begin ig_shift_data <= { ig_shift_data[23:0], 8'hxx }; ig_shift_valid <= { ig_shift_valid[2:0], 1'b0 }; end end // Feed data FIFO assign df_wdata = ig_shift_data[31:24]; assign df_we = ig_shift_valid[3]; // FIFOs // ----- // Data // Make it 2048 so we can easily store a whole max length // packet (1023 bytes data + 2 bytes CRC + 4 byte header) // while we're waiting to receive its length fifo_sync_ram #( .DEPTH(2048), .WIDTH(8) ) data_fifo_I ( .wr_data (df_wdata), .wr_ena (df_we), .wr_full (df_full), .rd_data (df_rdata), .rd_ena (df_re), .rd_empty (df_empty), .clk (clk), .rst (rst) ); // Packet Length bypass ( + final status in MSB ) fifo_sync_shift #( .DEPTH(2), .WIDTH(12) ) len_fifo_I ( .wr_data (lf_wdata), .wr_ena (lf_we), .wr_full (lf_full), .rd_data (lf_rdata), .rd_ena (lf_re), .rd_empty (lf_empty), .clk (clk), .rst (rst) ); // Egress logic // ------------ // State register always @(posedge clk) if (rst) eg_state <= EST_IDLE; else eg_state <= eg_state_nxt; // Next-State always @(*) begin // Default eg_state_nxt = eg_state; // Transitions case (eg_state) EST_IDLE: if (!df_empty) eg_state_nxt = EST_WAIT_LEN; EST_WAIT_LEN: if (!lf_empty | !eg_pid_data) eg_state_nxt = EST_HDR_0; EST_HDR_0: if (!df_empty) eg_state_nxt = EST_HDR_1; EST_HDR_1: if (!df_empty) eg_state_nxt = EST_HDR_2; EST_HDR_2: if (!df_empty) eg_state_nxt = EST_HDR_3; EST_HDR_3: if (!df_empty) eg_state_nxt = (!eg_pid_data | eg_len_last) ? EST_IDLE : EST_DATA_OUT; EST_DATA_OUT: eg_state_nxt = eg_len_last ? EST_IDLE : EST_DATA_PAUSE; EST_DATA_PAUSE: eg_state_nxt = EST_DATA_OUT; endcase end // Length counter always @(posedge clk) if (eg_state == EST_WAIT_LEN) eg_len <= { 1'b0, lf_rdata[10:0] } - 2; else if (eg_state == EST_DATA_OUT) eg_len <= eg_len - 1; assign eg_len_last = eg_len[11]; // Packet with data payload always @(posedge clk) if (eg_state == EST_IDLE) eg_pid_data <= (df_rdata[7:4] == PID_DATA0) | (df_rdata[7:4] == PID_DATA1); // Data FIFO read assign df_re = ~df_empty & ( (eg_state == EST_HDR_0) | (eg_state == EST_HDR_1) | (eg_state == EST_HDR_2) | (eg_state == EST_HDR_3) | (eg_state == EST_DATA_OUT) ); // Length FIFO read assign lf_re = eg_pid_data & (eg_state == EST_HDR_1); // Stream output always @(posedge clk) begin // Default stream_stb <= 1'b0; stream_data <= 8'hxx; // Depends on state case (eg_state) EST_HDR_0: begin stream_stb <= 1'b1; stream_data <= eg_pid_data ? { df_rdata[7:4], lf_rdata[11:8] } : df_rdata; end EST_HDR_1: begin stream_stb <= 1'b1; stream_data <= eg_pid_data ? lf_rdata[7:0] : df_rdata; end EST_HDR_2: begin stream_stb <= 1'b1; stream_data <= df_rdata; end EST_HDR_3: begin stream_stb <= 1'b1; stream_data <= df_rdata; end EST_DATA_OUT: begin stream_stb <= 1'b1; stream_data <= df_rdata; end endcase end endmodule // ut_pkt_stream