SummerCart64/fw/rtl/usb/usb_pc.v

467 lines
15 KiB
Verilog

module usb_pc (
input i_clk,
input i_reset,
output o_ftdi_clk,
output o_ftdi_si,
input i_ftdi_so,
input i_ftdi_cts,
output reg o_request,
output reg o_write,
input i_busy,
input i_ack,
output reg [3:0] o_bank,
output reg [25:0] o_address,
input [31:0] i_data,
output reg [31:0] o_data,
input i_debug_start,
output o_debug_busy,
input [3:0] i_debug_bank,
input [23:0] i_debug_address,
input [19:0] i_debug_length,
input i_debug_fifo_request,
input i_debug_fifo_flush,
output [10:0] o_debug_fifo_items,
output [31:0] o_debug_fifo_data
);
// Module parameters
parameter byte VERSION = "a";
// FTDI transport
reg r_ftdi_rx_ready;
wire w_ftdi_rx_valid;
wire [7:0] w_ftdi_rx_data;
wire w_ftdi_tx_busy;
reg r_ftdi_tx_valid;
reg [7:0] r_ftdi_tx_data;
usb_ftdi_fsi usb_ftdi_fsi_inst (
.i_clk(i_clk),
.i_reset(i_reset),
.o_ftdi_clk(o_ftdi_clk),
.o_ftdi_si(o_ftdi_si),
.i_ftdi_so(i_ftdi_so),
.i_ftdi_cts(i_ftdi_cts),
.i_rx_ready(r_ftdi_rx_ready),
.o_rx_valid(w_ftdi_rx_valid),
.o_rx_data(w_ftdi_rx_data),
.o_tx_busy(w_ftdi_tx_busy),
.i_tx_valid(r_ftdi_tx_valid),
.i_tx_channel(1'b1), // Channel B
.i_tx_data(r_ftdi_tx_data)
);
// Debug FIFO
wire w_fifo_usb_full;
wire [9:0] w_fifo_usb_items;
reg r_fifo_usb_write_request;
reg [31:0] r_fifo_usb_data;
assign o_debug_fifo_items = {w_fifo_usb_full, w_fifo_usb_items};
fifo_usb fifo_usb_inst (
.clock(i_clk),
.sclr(i_debug_fifo_flush),
.full(w_fifo_usb_full),
.usedw(w_fifo_usb_items),
.wrreq(r_fifo_usb_write_request),
.data(r_fifo_usb_data),
.rdreq(i_debug_fifo_request),
.q(o_debug_fifo_data)
);
// Command ids
localparam byte CMD_TRIGGER [0:2] = '{"C", "M", "D"};
localparam byte CMD_IDENTIFY = "I";
localparam byte CMD_WRITE = "W";
localparam byte CMD_DEBUG_WRITE = "D";
localparam byte CMD_DEBUG_SEND = "Q";
localparam [1:0] RX_STAGE_CMD = 2'd0;
localparam [1:0] RX_STAGE_PARAM = 2'd1;
localparam [1:0] RX_STAGE_DATA = 2'd2;
localparam [1:0] RX_STAGE_IGNORE = 2'd3;
// RX module
reg [1:0] r_rx_stage;
reg [2:0] r_rx_byte_counter;
reg [7:0] r_rx_cmd;
reg r_rx_buffer_valid;
reg r_rx_param_valid;
reg [63:0] r_rx_buffer;
reg [19:0] r_data_items_remaining;
reg r_tx_cmd_valid;
reg [7:0] r_tx_cmd;
reg r_tx_done;
always @(posedge i_clk) begin
r_rx_buffer_valid <= 1'b0;
r_rx_param_valid <= 1'b0;
r_tx_cmd_valid <= 1'b0;
if (i_reset) begin
r_rx_stage <= RX_STAGE_CMD;
r_rx_byte_counter <= 3'd0;
end else begin
if (i_debug_start) begin
r_rx_stage <= RX_STAGE_IGNORE;
r_rx_cmd <= CMD_DEBUG_SEND;
r_tx_cmd <= CMD_DEBUG_SEND;
r_tx_cmd_valid <= 1'b1;
end
if (w_ftdi_rx_valid) begin
r_rx_byte_counter <= r_rx_byte_counter + 3'd1;
case (r_rx_stage)
RX_STAGE_CMD: begin
if (w_ftdi_rx_data != CMD_TRIGGER[r_rx_byte_counter[1:0]]) begin
r_rx_byte_counter <= 3'd0;
end
if (r_rx_byte_counter == 3'd3) begin
r_rx_byte_counter <= 3'd0;
r_rx_cmd <= w_ftdi_rx_data;
case (w_ftdi_rx_data)
CMD_IDENTIFY: begin
r_rx_stage <= RX_STAGE_IGNORE;
r_tx_cmd_valid <= 1'b1;
r_tx_cmd <= w_ftdi_rx_data;
end
CMD_WRITE: r_rx_stage <= RX_STAGE_PARAM;
CMD_DEBUG_WRITE: r_rx_stage <= RX_STAGE_PARAM;
default: r_rx_stage <= RX_STAGE_CMD;
endcase
end
end
RX_STAGE_PARAM: begin
r_rx_buffer <= {r_rx_buffer[55:0], w_ftdi_rx_data};
case (r_rx_cmd)
CMD_WRITE: begin
if (r_rx_byte_counter == 3'd7) begin
r_rx_stage <= RX_STAGE_DATA;
r_rx_byte_counter <= 3'd0;
r_rx_param_valid <= 1'b1;
end
end
CMD_DEBUG_WRITE: begin
if (r_rx_byte_counter == 3'd3) begin
r_rx_stage <= RX_STAGE_DATA;
r_rx_byte_counter <= 3'd0;
r_rx_param_valid <= 1'b1;
end
end
default: begin
r_rx_stage <= RX_STAGE_CMD;
r_rx_byte_counter <= 3'd0;
end
endcase
end
RX_STAGE_DATA: begin
r_rx_buffer <= {r_rx_buffer[55:0], w_ftdi_rx_data};
case (r_rx_cmd)
CMD_WRITE: begin
if (r_rx_byte_counter == 3'd3) begin
if (r_data_items_remaining == 20'd0) begin
r_rx_stage <= RX_STAGE_IGNORE;
r_tx_cmd_valid <= 1'b1;
r_tx_cmd <= r_rx_cmd;
end
r_rx_byte_counter <= 3'd0;
r_rx_buffer_valid <= 1'b1;
end
end
CMD_DEBUG_WRITE: begin
if (r_rx_byte_counter == 3'd3) begin
if (r_data_items_remaining == 20'd0) begin
r_rx_stage <= RX_STAGE_CMD;
end
r_rx_byte_counter <= 3'd0;
r_rx_buffer_valid <= 1'b1;
end
end
default: begin
r_rx_stage <= RX_STAGE_CMD;
r_rx_byte_counter <= 3'd0;
end
endcase
end
RX_STAGE_IGNORE: begin
end
default: begin
r_rx_stage <= RX_STAGE_CMD;
r_rx_byte_counter <= 3'd0;
end
endcase
end
if (r_tx_done) begin
r_rx_stage <= RX_STAGE_CMD;
r_rx_byte_counter <= 3'd0;
end
end
end
// Command parameter decoder
always @(posedge i_clk) begin
if (i_reset) begin
end else begin
if (r_rx_param_valid) begin
case (r_rx_cmd)
CMD_WRITE: begin
o_address <= {r_rx_buffer[57:34], 2'b00};
o_bank <= r_rx_buffer[27:24];
r_data_items_remaining <= r_rx_buffer[19:0];
end
CMD_DEBUG_WRITE: begin
r_data_items_remaining <= r_rx_buffer[19:0];
end
endcase
end
if (r_tx_cmd_valid && r_tx_cmd == CMD_DEBUG_SEND) begin
o_bank <= i_debug_bank;
o_address <= {i_debug_address, 2'b00};
r_data_items_remaining <= i_debug_length;
end
if (o_request && !i_busy && r_data_items_remaining > 20'd0) begin
o_address[25:2] <= o_address[25:2] + 1'd1;
r_data_items_remaining <= r_data_items_remaining - 1'd1;
end
if (r_fifo_usb_write_request && r_data_items_remaining > 20'd0) begin
r_data_items_remaining <= r_data_items_remaining - 1'd1;
end
end
end
// TX module
localparam byte IDENTIFY_STRING [0:3] = '{"S", "6", "4", VERSION};
localparam byte RSP_COMPLETE [0:2] = '{"C", "M", "P"};
localparam [1:0] TX_STAGE_IDLE = 2'd0;
localparam [1:0] TX_STAGE_DATA = 2'd1;
localparam [1:0] TX_STAGE_RESPONSE = 2'd2;
reg [1:0] r_tx_stage;
reg [1:0] r_tx_byte_counter;
reg [31:0] r_i_data_buffer;
always @(*) begin
r_ftdi_tx_data = 8'h00;
case (r_tx_stage)
TX_STAGE_DATA: begin
case (r_tx_cmd)
CMD_IDENTIFY: r_ftdi_tx_data = IDENTIFY_STRING[r_tx_byte_counter];
CMD_DEBUG_SEND: r_ftdi_tx_data = r_i_data_buffer[(((4 - r_tx_byte_counter) * 8) - 1) -: 8];
endcase
end
TX_STAGE_RESPONSE: begin
if (r_tx_byte_counter != 2'd3) r_ftdi_tx_data = RSP_COMPLETE[r_tx_byte_counter];
else r_ftdi_tx_data = r_tx_cmd;
end
endcase
end
wire w_tx_successful = r_ftdi_tx_valid && !w_ftdi_tx_busy;
reg r_bus_data_request;
reg r_bus_data_valid;
reg r_bus_data_feisable;
assign o_debug_busy = r_tx_stage == TX_STAGE_DATA && r_tx_cmd == CMD_DEBUG_SEND;
always @(posedge i_clk) begin
r_ftdi_tx_valid <= 1'b0;
r_tx_done <= 1'b0;
r_bus_data_request <= 1'b0;
if (i_reset) begin
r_tx_stage <= TX_STAGE_IDLE;
end else begin
if (w_tx_successful) r_tx_byte_counter <= r_tx_byte_counter + 2'd1;
case (r_tx_stage)
TX_STAGE_IDLE: begin
r_tx_byte_counter <= 2'd0;
if (r_tx_cmd_valid) begin
case (r_tx_cmd)
CMD_IDENTIFY: begin
r_tx_stage <= TX_STAGE_DATA;
r_ftdi_tx_valid <= 1'b1;
end
CMD_DEBUG_SEND: begin
r_bus_data_request <= 1'b1;
r_tx_stage <= TX_STAGE_DATA;
end
default: begin
r_tx_stage <= TX_STAGE_RESPONSE;
r_ftdi_tx_valid <= 1'b1;
end
endcase
end
end
TX_STAGE_DATA: begin
r_ftdi_tx_valid <= 1'b1;
if (r_tx_byte_counter == 2'd3 && w_tx_successful) begin
case (r_tx_cmd)
CMD_IDENTIFY: begin
r_tx_stage <= TX_STAGE_RESPONSE;
end
endcase
end
if (r_tx_cmd == CMD_DEBUG_SEND) begin
r_ftdi_tx_valid <= 1'b0;
if (r_bus_data_valid) begin
r_bus_data_feisable <= 1'b1;
end
if (r_bus_data_feisable) begin
r_ftdi_tx_valid <= 1'b1;
end
if (w_tx_successful) begin
if (r_tx_byte_counter == 2'd3 && r_bus_data_feisable) begin
r_bus_data_request <= 1'b1;
r_bus_data_feisable <= 1'b0;
end
end
if (w_tx_successful && r_data_items_remaining == 20'd0 && r_tx_byte_counter == 2'd3) begin
r_bus_data_request <= 1'b0;
r_tx_stage <= TX_STAGE_IDLE;
r_tx_done <= 1'b1;
r_bus_data_feisable <= 1'b0;
end
end
end
TX_STAGE_RESPONSE: begin
r_ftdi_tx_valid <= 1'b1;
if (r_tx_byte_counter == 2'd3 && w_tx_successful) begin
r_tx_stage <= TX_STAGE_IDLE;
r_tx_done <= 1'b1;
end
end
default: r_tx_stage <= TX_STAGE_IDLE;
endcase
end
end
// Bus controller
always @(posedge i_clk) begin
o_request <= 1'b0;
o_write <= 1'b0;
r_bus_data_valid <= 1'b0;
r_fifo_usb_write_request <= 1'b0;
if (i_reset) begin
r_ftdi_rx_ready <= 1'b1;
end else begin
case (r_rx_cmd)
CMD_WRITE: begin
if ((r_rx_buffer_valid || !r_ftdi_rx_ready) && !i_busy) begin
o_request <= 1'b1;
o_write <= 1'b1;
o_data <= r_rx_buffer[31:0];
r_ftdi_rx_ready <= 1'b1;
end
if (o_request && i_busy) begin
o_request <= 1'b1;
o_write <= 1'b1;
end
if (r_rx_buffer_valid && i_busy) begin
r_ftdi_rx_ready <= 1'b0;
end
end
CMD_DEBUG_WRITE: begin
if (r_rx_buffer_valid) begin
r_fifo_usb_data <= r_rx_buffer[31:0];
r_fifo_usb_write_request <= 1'b1;
end
if (w_fifo_usb_full) begin
r_ftdi_rx_ready <= 1'b0;
end else begin
r_ftdi_rx_ready <= 1'b1;
end
end
endcase
if (r_bus_data_request) begin
o_request <= 1'b1;
end
if (o_request && i_busy) begin
o_request <= 1'b1;
end
if (i_ack) begin
r_i_data_buffer <= i_data;
r_bus_data_valid <= 1'b1;
end
end
end
endmodule