mirror of
https://github.com/Polprzewodnikowy/SummerCart64.git
synced 2024-12-28 14:01:54 +01:00
471 lines
15 KiB
Verilog
471 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_channel(1'bZ),
|
|
.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 - {2'b00, r_tx_byte_counter}) * 8) - 1) -: 8];
|
|
default: begin end
|
|
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
|
|
|
|
default: begin 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
|