SummerCart64/fw/rtl/usb/usb_ft1248.sv
Polprzewodnikowy 0db5e5c59f nice
2021-08-15 21:49:02 +02:00

231 lines
6.5 KiB
Systemverilog

module usb_ft1248 (
if_system.sys system_if,
output usb_clk,
output usb_cs,
input usb_miso,
inout [3:0] usb_miosi,
input rx_flush,
output rx_empty,
output rx_almost_empty,
input rx_read,
output [7:0] rx_rdata,
input tx_flush,
output tx_full,
output tx_almost_full,
input tx_write,
input [7:0] tx_wdata
);
// FIFOs
wire rx_full;
wire rx_almost_full;
reg rx_write;
reg [7:0] rx_wdata;
wire tx_empty;
reg tx_read;
wire [7:0] tx_rdata;
fifo8 fifo_8_rx_inst (
.clock(system_if.clk),
.sclr(rx_flush),
.empty(rx_empty),
.almost_empty(rx_almost_empty),
.rdreq(rx_read),
.q(rx_rdata),
.full(rx_full),
.almost_full(rx_almost_full),
.wrreq(rx_write),
.data(rx_wdata)
);
fifo8 fifo_8_tx_inst (
.clock(system_if.clk),
.sclr(tx_flush),
.empty(tx_empty),
.rdreq(tx_read),
.q(tx_rdata),
.full(tx_full),
.almost_full(tx_almost_full),
.wrreq(tx_write),
.data(tx_wdata)
);
// FT1248 interface controller
// Constants definition
typedef enum bit [2:0] {
S_TRY_RX = 3'b000,
S_TRY_TX = 3'b001,
S_COMMAND = 3'b100,
S_DATA = 3'b101,
S_END = 3'b111
} e_state;
typedef enum bit [7:0] {
C_WRITE = 8'h00,
C_READ = 8'h04
} e_command;
// FSM state
e_state state;
// Clock divider and generator
reg [3:0] clock_divider;
wire rising_edge = clock_divider[1];
always_ff @(posedge system_if.clk) begin
if (system_if.reset || state == S_TRY_RX || state == S_TRY_TX) begin
clock_divider <= 4'b0001;
end else begin
clock_divider <= {clock_divider[2:0], clock_divider[3]};
end
end
// Output chip select and data register behavior
reg [1:0] bit_counter;
reg [7:0] tx_buffer;
wire clk_data;
reg cs_data;
wire [3:0] miosi_data;
reg miosi_output_enable_data;
reg clk_output;
reg cs_output;
reg [3:0] miosi_input;
reg [3:0] miosi_output;
reg miosi_output_enable;
reg miso_input;
always_comb begin
clk_data = 1'b0;
if (state == S_COMMAND || state == S_DATA) clk_data = (clock_divider[2] | clock_divider[3]);
usb_clk = clk_output;
usb_cs = cs_output;
miosi_data = bit_counter[0] ? tx_buffer[3:0] : tx_buffer[7:4];
usb_miosi = miosi_output_enable ? miosi_output : 4'bZZZZ;
end
always_ff @(posedge system_if.clk) begin
clk_output <= clk_data;
cs_output <= cs_data;
miosi_input <= usb_miosi;
miosi_output <= miosi_data;
miosi_output_enable <= miosi_output_enable_data;
miso_input <= usb_miso;
end
// FSM
reg is_write;
always_ff @(posedge system_if.clk) begin
rx_write <= 1'b0;
tx_read <= 1'b0;
if (system_if.reset) begin
state <= S_TRY_RX;
cs_data <= 1'b1;
miosi_output_enable_data <= 1'b0;
end else begin
case (state)
S_TRY_RX: begin
if (!rx_full) begin
state <= S_COMMAND;
tx_buffer <= C_READ;
bit_counter <= 2'b11;
is_write <= 1'b0;
end else begin
state <= S_TRY_TX;
end
end
S_TRY_TX: begin
if (!tx_empty) begin
state <= S_COMMAND;
tx_buffer <= C_WRITE;
bit_counter <= 2'b11;
is_write <= 1'b1;
end else begin
state <= S_TRY_RX;
end
end
S_COMMAND: begin
cs_data <= 1'b0;
if (rising_edge) begin
bit_counter <= bit_counter + 1'd1;
miosi_output_enable_data <= 1'b1;
if (bit_counter == 2'd1) begin
miosi_output_enable_data <= 1'b0;
end
if (bit_counter == 2'd2) begin
if (!miso_input) begin
state <= S_DATA;
bit_counter <= 2'b11;
tx_buffer <= tx_rdata;
miosi_output_enable_data <= is_write;
end else begin
state <= S_END;
miosi_output_enable_data <= 1'b0;
end
end
end
end
S_DATA: begin
if (rising_edge) begin
bit_counter <= {1'b0, ~bit_counter[0]};
miosi_output_enable_data <= is_write;
rx_wdata <= {miosi_input, rx_wdata[7:4]};
if (bit_counter == 1'd1) begin
tx_buffer <= tx_rdata;
end
if (!is_write && (bit_counter[0] == 1'd0)) begin
rx_write <= 1'b1;
end
if (is_write && (bit_counter[0] == 1'd1)) begin
tx_read <= 1'b1;
end
if (
(bit_counter[0] == 1'd1 && miso_input) ||
(bit_counter[0] == 1'd0 && (
(is_write && tx_empty) ||
(!is_write && rx_almost_full)
))) begin
state <= S_END;
miosi_output_enable_data <= 1'b0;
end
end
end
S_END: begin
cs_data <= 1'b1;
state <= is_write ? S_TRY_RX : S_TRY_TX;
end
default: begin
state <= S_TRY_RX;
cs_data <= 1'b1;
miosi_output_enable_data <= 1'b0;
end
endcase
end
end
endmodule