mirror of
https://github.com/Polprzewodnikowy/SummerCart64.git
synced 2024-11-22 22:19:14 +01:00
380 lines
12 KiB
Verilog
380 lines
12 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
|
|
);
|
|
|
|
// 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)
|
|
);
|
|
|
|
|
|
// Command ids
|
|
|
|
localparam byte CMD_TRIGGER [0:2] = '{"C", "M", "D"};
|
|
|
|
localparam byte CMD_IDENTIFY = "I";
|
|
localparam byte CMD_READ = "R";
|
|
localparam byte CMD_WRITE = "W";
|
|
localparam byte CMD_DEBUG_MODE = "D";
|
|
localparam byte CMD_DEBUG_WRITE = "F";
|
|
|
|
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 (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]) 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_READ: r_rx_stage <= RX_STAGE_PARAM;
|
|
|
|
CMD_WRITE: r_rx_stage <= RX_STAGE_PARAM;
|
|
|
|
CMD_DEBUG_MODE: r_rx_stage <= RX_STAGE_PARAM;
|
|
|
|
CMD_DEBUG_WRITE: r_rx_stage <= RX_STAGE_DATA;
|
|
|
|
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_READ: begin
|
|
if (r_rx_byte_counter == 3'd7) begin
|
|
r_rx_stage <= RX_STAGE_IGNORE;
|
|
r_rx_byte_counter <= 3'd0;
|
|
r_rx_param_valid <= 1'b1;
|
|
r_tx_cmd_valid <= 1'b1;
|
|
r_tx_cmd <= r_rx_cmd;
|
|
end
|
|
end
|
|
|
|
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_MODE: begin
|
|
if (r_rx_byte_counter == 3'd3) begin
|
|
r_rx_stage <= RX_STAGE_CMD;
|
|
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_data_items_remaining == 20'd0) begin
|
|
r_rx_stage <= RX_STAGE_CMD;
|
|
end
|
|
r_rx_buffer_valid <= 1'b1;
|
|
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
|
|
|
|
reg r_tx_debug_enabled;
|
|
|
|
always @(posedge i_clk) begin
|
|
if (i_reset) begin
|
|
r_tx_debug_enabled <= 1'b0;
|
|
end else begin
|
|
if (r_rx_param_valid) begin
|
|
case (r_rx_cmd)
|
|
CMD_READ, CMD_WRITE: begin
|
|
o_address <= {r_rx_buffer[63:34], 2'b00};
|
|
o_bank <= r_rx_buffer[27:24];
|
|
r_data_items_remaining <= r_rx_buffer[19:0];
|
|
end
|
|
|
|
CMD_DEBUG_MODE: begin
|
|
r_tx_debug_enabled <= r_rx_buffer[0];
|
|
end
|
|
|
|
CMD_DEBUG_WRITE: begin
|
|
r_data_items_remaining <= r_rx_buffer[19:0];
|
|
end
|
|
endcase
|
|
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
|
|
end
|
|
end
|
|
|
|
|
|
// TX module
|
|
|
|
localparam byte IDENTIFY_STRING [0:3] = '{"S", "6", "4", "a"};
|
|
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_READ: r_ftdi_tx_data = r_i_data_buffer[(r_tx_byte_counter * 8) -: 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;
|
|
|
|
always @(posedge i_clk) begin
|
|
r_ftdi_tx_valid <= 1'b0;
|
|
r_tx_done <= 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
|
|
r_ftdi_tx_valid <= 1'b1;
|
|
|
|
case (r_tx_cmd)
|
|
CMD_IDENTIFY: r_tx_stage <= TX_STAGE_DATA;
|
|
default: r_tx_stage <= TX_STAGE_RESPONSE;
|
|
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
|
|
|
|
CMD_READ: begin
|
|
if (r_data_items_remaining == 20'd0) begin
|
|
r_tx_stage <= TX_STAGE_RESPONSE;
|
|
end
|
|
end
|
|
|
|
default: r_tx_stage <= TX_STAGE_RESPONSE;
|
|
endcase
|
|
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;
|
|
|
|
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
|
|
endcase
|
|
|
|
// TODO: Read from bus
|
|
// case (r_tx_cmd)
|
|
// CMD_READ: begin
|
|
// end
|
|
// endcase
|
|
end
|
|
end
|
|
|
|
endmodule
|