SummerCart64/fw/rtl/usb/usb_ft1248.sv

155 lines
4.4 KiB
Systemverilog
Raw Normal View History

2021-08-12 21:07:47 +02:00
module usb_ft1248 (
if_system.sys system_if,
output usb_clk,
output usb_cs,
input usb_miso,
inout [3:0] usb_miosi,
input request,
input write,
output busy,
output reg ack,
input [7:0] wdata,
output reg [7:0] rdata,
output reg rx_available,
output reg tx_available
);
typedef enum bit [2:0] {
S_IDLE = 3'b000,
S_COMMAND = 3'b100,
S_DATA = 3'b101,
S_END = 3'b110
} e_state;
typedef enum bit [7:0] {
C_WRITE = 8'h00,
C_READ = 8'h04
} e_command;
e_state state;
assign busy = state[2];
reg [3:0] clock_divider;
wire rising_edge;
wire falling_edge;
assign rising_edge = clock_divider[1];
assign falling_edge = clock_divider[3];
always_ff @(posedge system_if.clk) begin
if (system_if.reset || state == S_IDLE) begin
clock_divider <= 4'b0001;
usb_clk <= 1'b0;
end else begin
clock_divider <= {clock_divider[2:0], clock_divider[3]};
if (state != S_END) begin
if (rising_edge) usb_clk <= 1'b1;
if (falling_edge) usb_clk <= 1'b0;
end
end
end
reg miosi_ff_1, miosi_ff_2;
reg miso_ff_1, miso_ff_2;
reg mode;
reg [1:0] bit_counter;
reg output_enable;
reg [7:0] tx_data;
reg [3:0] miosi;
reg [7:0] command;
assign usb_miosi = output_enable ? miosi : 4'bZZZZ;
always_ff @(posedge system_if.clk) begin
{miosi_ff_2, miosi_ff_1} <= {miosi_ff_1, usb_miosi[0]};
{miso_ff_2, miso_ff_1} <= {miso_ff_1, usb_miso};
rx_available <= 1'b0;
tx_available <= 1'b0;
ack <= 1'b0;
if (system_if.reset) begin
state <= S_IDLE;
usb_cs <= 1'b1;
output_enable <= 1'b0;
end else begin
case (state)
S_IDLE: begin
rx_available <= ~miso_ff_2;
tx_available <= ~miosi_ff_2;
bit_counter <= 1'b0;
usb_cs <= 1'b1;
output_enable <= 1'b0;
if (request) begin
mode <= write;
if (rx_available || (write && tx_available)) begin
state <= S_COMMAND;
command <= write ? C_WRITE : C_READ;
usb_cs <= 1'b0;
rx_available <= 1'b0;
tx_available <= 1'b0;
tx_data <= {wdata[3:0], wdata[7:4]};
end
end
end
S_COMMAND: begin
if (rising_edge) begin
output_enable <= 1'b1;
bit_counter <= bit_counter + 1'd1;
{miosi, command} <= {command, 4'bXXXX};
if (bit_counter == 2'd2) begin
output_enable <= 1'b0;
end
end
if (falling_edge && (bit_counter == 2'd3)) begin
state <= S_DATA;
bit_counter <= 2'd0;
end
end
S_DATA: begin
if (rising_edge) begin
output_enable <= mode;
bit_counter[0] <= ~bit_counter[0];
{miosi, tx_data} <= {tx_data, 4'bXXXX};
end
if (falling_edge) begin
rdata <= {usb_miosi, rdata[7:4]};
if (!bit_counter[0]) begin
state <= S_END;
output_enable <= 1'b0;
ack <= 1'b1;
end
end
end
S_END: begin
if (rising_edge) begin
bit_counter <= bit_counter + 1'd1;
if (bit_counter == 2'd3) begin
state <= S_IDLE;
end
usb_cs <= 1'b1;
end
end
default: begin
state <= S_IDLE;
end
endcase
end
end
endmodule