blob: 7d37f098170eaa5322426b143ef566a47b67e936 [file] [log] [blame]
// Copyright lowRISC contributors.
// Copyright Luke Valenty (TinyFPGA project)
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// Converted from common/usb_serial_ctrl_ep.v
// -- move from CDC to Google simple serial protocol
// -- conform to lowRISC coding style
module usb_serial_ctrl_ep #(
parameter int unsigned MaxPktSizeByte = 32,
// Derived parameters
localparam int unsigned PktW = $clog2(MaxPktSizeByte)
) (
input clk_i,
input rst_ni,
output logic [6:0] dev_addr,
////////////////////////////
// out endpoint interface //
////////////////////////////
input out_ep_data_put_i,
input [PktW - 1:0] out_ep_put_addr_i,
input [7:0] out_ep_data_i,
input out_ep_acked_i,
input out_ep_rollback_i,
input out_ep_setup_i,
output logic out_ep_full_o,
output logic out_ep_stall_o,
///////////////////////////
// in endpoint interface //
///////////////////////////
input in_ep_rollback_i,
input in_ep_acked_i,
input [PktW - 1:0] in_ep_get_addr_i,
input in_ep_data_get_i,
output logic in_ep_stall_o,
output logic in_ep_has_data_o,
output logic [7:0] in_ep_data_o,
output logic in_ep_data_done_o
);
// suppress errors
logic unused_1;
logic [PktW-1:0] unused_2;
assign unused_1 = in_ep_rollback_i;
assign unused_2 = in_ep_get_addr_i;
import usb_consts_pkg::*;
// State machine for control transfers
typedef enum logic [2:0] {
StIdle = 3'h0,
StSetup = 3'h1,
StDataIn = 3'h2,
StDataOut = 3'h3,
StStatusIn = 3'h4,
StStatusOut = 3'h5
} state_ctrl_xfr_e;
state_ctrl_xfr_e ctrl_xfr_state;
state_ctrl_xfr_e ctrl_xfr_state_next;
logic setup_stage_end;
logic status_stage_end;
logic send_zero_length_data_pkt;
// the default control endpoint gets assigned the device address
logic [6:0] dev_addr_int;
logic [6:0] new_dev_addr;
assign dev_addr = dev_addr_int;
assign out_ep_stall_o = 1'b0;
assign out_ep_full_o = 1'b0;
// keep track of new out data start and end
logic pkt_start;
logic pkt_end;
assign pkt_start = (out_ep_put_addr_i == 0) && out_ep_data_put_i;
assign pkt_end = out_ep_acked_i || out_ep_rollback_i;
// need to record the 8 bytes of setup data
logic [7:0] bmRequestType, raw_setup_data [8];
// Alias for the setup bytes using names from USB spec
usb_setup_request_e bRequest;
logic [15:0] wValue, wLength, wIndex;
logic setup_pkt_start, has_data_stage, out_data_stage, in_data_stage;
assign setup_pkt_start = pkt_start && out_ep_setup_i;
assign has_data_stage = wLength != 16'h0;
assign out_data_stage = has_data_stage && !bmRequestType[7];
assign in_data_stage = has_data_stage && bmRequestType[7];
logic [7:0] bytes_sent;
logic [6:0] rom_length;
logic all_data_sent, more_data_to_send, in_data_transfer_done;
// if any upper bits in wLength are set the rom_length will trigger first
// here any request for >127 will generate a check based on >128
assign all_data_sent = (bytes_sent >= {1'b0, rom_length}) ||
(bytes_sent >= {|wLength[15:7], wLength[6:0]});
assign more_data_to_send = !all_data_sent;
rising_edge_detector detect_in_data_transfer_done (
.clk_i (clk_i),
.rst_ni(rst_ni),
.in_i (all_data_sent),
.out_o (in_data_transfer_done)
);
assign in_ep_has_data_o = more_data_to_send || send_zero_length_data_pkt;
assign in_ep_data_done_o = (in_data_transfer_done && (ctrl_xfr_state == StDataIn)) ||
send_zero_length_data_pkt;
logic [6:0] rom_addr;
logic save_dev_addr;
////////////////////////////////////
// control transfer state machine //
////////////////////////////////////
always_comb begin
setup_stage_end = 1'b0;
status_stage_end = 1'b0;
send_zero_length_data_pkt = 1'b0;
unique case (ctrl_xfr_state)
StIdle: begin
if (setup_pkt_start) begin
ctrl_xfr_state_next = StSetup;
end else begin
ctrl_xfr_state_next = StIdle;
end
end
StSetup: begin
if (pkt_end) begin
// rollback here is most likely a CRC error on the SETUP packet
if (out_ep_rollback_i) begin
ctrl_xfr_state_next = StIdle;
end else if (in_data_stage) begin
ctrl_xfr_state_next = StDataIn;
setup_stage_end = 1'b1;
end else if (out_data_stage) begin
ctrl_xfr_state_next = StDataOut;
setup_stage_end = 1'b1;
end else begin
ctrl_xfr_state_next = StStatusIn;
send_zero_length_data_pkt = 1'b1;
setup_stage_end = 1'b1;
end
end else begin
ctrl_xfr_state_next = StSetup;
end
end
StDataIn: begin
if (in_ep_stall_o) begin
ctrl_xfr_state_next = StIdle;
status_stage_end = 1'b1;
end else if (in_ep_acked_i && all_data_sent) begin
ctrl_xfr_state_next = StStatusOut;
end else begin
ctrl_xfr_state_next = StDataIn;
end
end
StDataOut: begin
if (out_ep_acked_i) begin
ctrl_xfr_state_next = StStatusIn;
send_zero_length_data_pkt = 1'b1;
end else begin
ctrl_xfr_state_next = StDataOut;
end
end
StStatusIn: begin
if (in_ep_acked_i) begin
ctrl_xfr_state_next = StIdle;
status_stage_end = 1'b1;
end else begin
ctrl_xfr_state_next = StStatusIn;
send_zero_length_data_pkt = 1'b1;
end
end
StStatusOut: begin
if (out_ep_acked_i) begin
ctrl_xfr_state_next = StIdle;
status_stage_end = 1'b1;
end else begin
ctrl_xfr_state_next = StStatusOut;
end
end
default begin
ctrl_xfr_state_next = StIdle;
end
endcase
end
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
ctrl_xfr_state <= StIdle;
end else begin
ctrl_xfr_state <= ctrl_xfr_state_next;
end
end
assign bmRequestType = raw_setup_data[0];
assign bRequest = usb_setup_request_e'(raw_setup_data[1]);
assign wValue = {raw_setup_data[3][7:0], raw_setup_data[2][7:0]};
assign wIndex = {raw_setup_data[5][7:0], raw_setup_data[4][7:0]};
assign wLength = {raw_setup_data[7][7:0], raw_setup_data[6][7:0]};
// suppress warning
logic [6:0] unused_bmR;
logic unused_wValue;
logic [15:0] unused_wIndex;
assign unused_bmR = bmRequestType[6:0];
assign unused_wValue = wValue[7];
assign unused_wIndex = wIndex;
// Check of upper put_addr bits needed because CRC will be sent (10 bytes total)
always_ff @(posedge clk_i) begin
if (out_ep_setup_i && out_ep_data_put_i && (out_ep_put_addr_i[PktW - 1:3] == '0)) begin
raw_setup_data[out_ep_put_addr_i[2:0]] <= out_ep_data_i;
end
end
// Send setup data (which will be empty in case of a SET operation and
// come from the ROM in the case of a GET)
usb_dscr_type_e dscr_type;
assign dscr_type = usb_dscr_type_e'(wValue[15:8]);
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
dev_addr_int <= '0;
save_dev_addr <= 1'b0;
in_ep_stall_o <= 1'b0;
end else begin
if (setup_stage_end) begin
bytes_sent <= '0;
// Command (bRequest) and sub-command (wValue) come from USB spec
unique case (bRequest)
SetupGetDescriptor: begin
unique case (dscr_type)
DscrTypeDevice: begin
in_ep_stall_o <= 1'b0;
rom_addr <= 7'h00;
rom_length <= 7'h12;
end
DscrTypeConfiguration: begin
in_ep_stall_o <= 1'b0;
rom_addr <= 7'h12;
rom_length <= 7'h20; // 9+9+7+7
end
DscrTypeDevQual: begin
in_ep_stall_o <= 1'b1;
rom_addr <= 7'h00;
rom_length <= 7'h00;
end
default begin
in_ep_stall_o <= 1'b0;
rom_addr <= 7'h00;
rom_length <= 7'h00;
end
endcase
end
SetupSetAddress: begin
in_ep_stall_o <= 1'b0;
rom_addr <= 7'h00;
rom_length <= 7'h00;
// we need to save the address after the status stage ends
// this is because the status stage token will still be using
// the old device address
save_dev_addr <= 1'b1;
new_dev_addr <= wValue[6:0];
end
SetupSetConfiguration: begin
in_ep_stall_o <= 1'b0;
rom_addr <= 7'h00;
rom_length <= 7'h00;
end
default begin
in_ep_stall_o <= 1'b0;
rom_addr <= 7'h00;
rom_length <= 7'h00;
end
endcase
end else if ((ctrl_xfr_state == StDataIn) && more_data_to_send && in_ep_data_get_i) begin
rom_addr <= rom_addr + 7'h1;
bytes_sent <= bytes_sent + 8'h1;
end else if (status_stage_end) begin
bytes_sent <= '0;
rom_addr <= '0;
rom_length <= '0;
if (save_dev_addr) begin
save_dev_addr <= 1'b0;
dev_addr_int <= new_dev_addr;
end
end
end
end
always_comb begin
unique case (rom_addr)
// device descriptor
'h000: in_ep_data_o = 8'd18; // bLength
'h001: in_ep_data_o = {DscrTypeDevice}; // bDescriptorType
'h002: in_ep_data_o = 8'h00; // bcdUSB[0]
'h003: in_ep_data_o = 8'h02; // bcdUSB[1]
'h004: in_ep_data_o = 8'h00; // bDeviceClass (defined at interface level)
'h005: in_ep_data_o = 8'h00; // bDeviceSubClass
'h006: in_ep_data_o = 8'h00; // bDeviceProtocol
'h007: in_ep_data_o = 8'd32; // bMaxPacketSize0
'h008: in_ep_data_o = 8'hd1; // idVendor[0] 0x18d1 Google Inc.
'h009: in_ep_data_o = 8'h18; // idVendor[1]
'h00A: in_ep_data_o = 8'h39; // idProduct[0] Simple Serial USB IP
'h00B: in_ep_data_o = 8'h50; // idProduct[1] (Allocated in Chrome OS block for this IP)
'h00C: in_ep_data_o = 8'h0; // bcdDevice[0]
'h00D: in_ep_data_o = 8'h1; // bcdDevice[1]
'h00E: in_ep_data_o = 8'h0; // iManufacturer
'h00F: in_ep_data_o = 8'h0; // iProduct
'h010: in_ep_data_o = 8'h0; // iSerialNumber
'h011: in_ep_data_o = 8'h1; // bNumConfigurations
// configuration descriptor
'h012: in_ep_data_o = 8'd9; // bLength
'h013: in_ep_data_o = {DscrTypeConfiguration}; // bDescriptorType
'h014: in_ep_data_o = 8'(9+9+7+7); // wTotalLength[0]
'h015: in_ep_data_o = 8'h0; // wTotalLength[1]
'h016: in_ep_data_o = 8'h1; // bNumInterfaces
'h017: in_ep_data_o = 8'h1; // bConfigurationValue
'h018: in_ep_data_o = 8'h0; // iConfiguration
'h019: in_ep_data_o = 8'hC0; // bmAttributes: must-be-one, self-powered
'h01A: in_ep_data_o = 8'd50; // bMaxPower
// interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12
'h01B: in_ep_data_o = 8'd9; // bLength
'h01C: in_ep_data_o = {DscrTypeInterface}; // bDescriptorType
'h01D: in_ep_data_o = 8'h0; // bInterfaceNumber
'h01E: in_ep_data_o = 8'h0; // bAlternateSetting
'h01F: in_ep_data_o = 8'h2; // bNumEndpoints (must follow below)
'h020: in_ep_data_o = 8'hff; // bInterfaceClass (Vendor Specific Class)
'h021: in_ep_data_o = 8'h50; // bInterfaceSubClass (Simple serial)
'h022: in_ep_data_o = 8'h1; // bInterfaceProtocol (standard)
'h023: in_ep_data_o = 8'h0; // iInterface
// endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13
'h024: in_ep_data_o = 8'd7; // bLength
'h025: in_ep_data_o = {DscrTypeEndpoint}; // bDescriptorType
'h026: in_ep_data_o = 8'h1; // bEndpointAddress, OUT
'h027: in_ep_data_o = 8'h02; // bmAttributes (0x02=bulk, data)
'h028: in_ep_data_o = 8'd32; // wMaxPacketSize[0]
'h029: in_ep_data_o = 8'h0; // wMaxPacketSize[1]
'h02A: in_ep_data_o = 8'h0; // bInterval
// endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13
'h02B: in_ep_data_o = 8'd7; // bLength
'h02C: in_ep_data_o = {DscrTypeEndpoint}; // bDescriptorType
'h02D: in_ep_data_o = 8'h81; // bEndpointAddress, IN
'h02E: in_ep_data_o = 8'h02; // bmAttributes (0x02=bulk, data)
'h02F: in_ep_data_o = 8'd32; // wMaxPacketSize[0]
'h030: in_ep_data_o = 8'h0; // wMaxPacketSize[1]
'h031: in_ep_data_o = 8'h4; // bInterval (4 vs 10 in twinkie)
default begin
in_ep_data_o = 0;
end
endcase
end
endmodule