interface usb_scb (); logic fifo_flush; logic reset_pending; logic reset_ack; logic write_buffer_flush; modport controller ( output fifo_flush, input reset_pending, output reset_ack, output write_buffer_flush ); modport usb ( input fifo_flush, output reset_pending, input reset_ack, input write_buffer_flush ); endinterface module usb_ft1248 ( input clk, input reset, usb_scb.usb usb_scb, fifo_bus.fifo fifo_bus, input usb_pwrsav, output logic usb_clk, output logic usb_cs, input usb_miso, inout [7:0] usb_miosi ); logic rx_full; logic rx_almost_full; logic rx_write; logic [7:0] rx_wdata; logic tx_empty; logic tx_almost_empty; logic tx_read; logic [7:0] tx_rdata; fifo_8kb fifo_8kb_rx_inst ( .clk(clk), .reset(reset || usb_scb.fifo_flush), .empty(fifo_bus.rx_empty), .almost_empty(fifo_bus.rx_almost_empty), .read(fifo_bus.rx_read), .rdata(fifo_bus.rx_rdata), .full(rx_full), .almost_full(rx_almost_full), .write(rx_write), .wdata(rx_wdata) ); fifo_8kb fifo_8kb_tx_inst ( .clk(clk), .reset(reset || usb_scb.fifo_flush), .empty(tx_empty), .almost_empty(tx_almost_empty), .read(tx_read), .rdata(tx_rdata), .full(fifo_bus.tx_full), .almost_full(fifo_bus.tx_almost_full), .write(fifo_bus.tx_write), .wdata(fifo_bus.tx_wdata) ); logic [1:0] usb_pwrsav_ff; logic [7:0] usb_miosi_out; logic usb_oe; logic ft_pwrsav; logic ft_clk; logic ft_cs; logic ft_miso; logic [7:0] ft_miosi_in; logic [7:0] ft_miosi_out; logic ft_oe; always_ff @(posedge clk) begin usb_pwrsav_ff <= {usb_pwrsav_ff[0], usb_pwrsav}; ft_pwrsav <= usb_pwrsav_ff[1]; usb_clk <= ft_clk; usb_cs <= ft_cs; ft_miso <= usb_miso; ft_miosi_in <= usb_miosi; usb_miosi_out <= ft_miosi_out; usb_oe <= ft_oe; end assign usb_miosi = usb_oe ? usb_miosi_out : 8'hZZ; typedef enum bit [2:0] { STATE_IDLE, STATE_SELECT, STATE_COMMAND, STATE_STATUS, STATE_DATA, STATE_DESELECT } e_state; typedef enum bit [7:0] { CMD_WRITE = 8'h00, CMD_READ = 8'h40, CMD_READ_MODEM_STATUS = 8'h20, CMD_WRITE_MODEM_STATUS = 8'h60, CMD_WRITE_BUFFER_FLUSH = 8'h08 } e_cmd; e_state state; e_state next_state; e_cmd cmd; e_cmd next_cmd; logic [3:0] phase; logic last_tx_failed; logic reset_reply; logic last_reset_status; logic [4:0] modem_status_counter; logic write_modem_status_pending; logic write_buffer_flush_pending; always_ff @(posedge clk) begin state <= next_state; cmd <= next_cmd; phase <= {phase[2:0], phase[3]}; if (state == STATE_IDLE) begin phase <= 4'b0100; end if (reset) begin last_tx_failed <= 1'b0; usb_scb.reset_pending <= 1'b0; last_reset_status <= 1'b0; modem_status_counter <= 5'd0; write_modem_status_pending <= 1'b0; write_buffer_flush_pending <= 1'b0; end else begin if (usb_scb.reset_ack) begin usb_scb.reset_pending <= 1'b0; reset_reply <= 1'b1; write_modem_status_pending <= 1'b1; end if (usb_scb.write_buffer_flush) begin write_buffer_flush_pending <= 1'b1; end if (state == STATE_IDLE) begin modem_status_counter <= modem_status_counter + 1'd1; end if ((state == STATE_DATA) && (cmd == CMD_WRITE) && phase[3]) begin last_tx_failed <= ft_miso; end if (!ft_miso && (state == STATE_DATA) && phase[3]) begin if (cmd == CMD_READ_MODEM_STATUS) begin last_reset_status <= ft_miosi_in[0]; if (!last_reset_status && ft_miosi_in[0]) begin usb_scb.reset_pending <= 1'b1; end if (last_reset_status && !ft_miosi_in[0]) begin reset_reply <= 1'b0; write_modem_status_pending <= 1'b1; end end if (cmd == CMD_WRITE_MODEM_STATUS) begin write_modem_status_pending <= 1'b0; end if (cmd == CMD_WRITE_BUFFER_FLUSH) begin write_buffer_flush_pending <= 1'b0; end end end end always_comb begin ft_clk = 1'b0; ft_cs = 1'b1; ft_miosi_out = 8'hFF; ft_oe = 1'b0; if (state == STATE_SELECT) begin ft_cs = 1'b0; end if (state == STATE_COMMAND) begin if (phase[0] || phase[1]) begin ft_clk = 1'b1; end ft_cs = 1'b0; ft_miosi_out = cmd; ft_oe = 1'b1; end if (state == STATE_STATUS) begin if (phase[0] || phase[1]) begin ft_clk = 1'b1; end ft_cs = 1'b0; end if (state == STATE_DATA) begin if (phase[0] || phase[1]) begin ft_clk = 1'b1; end ft_cs = 1'b0; if (cmd == CMD_WRITE) begin ft_miosi_out = tx_rdata; ft_oe = 1'b1; end if (cmd == CMD_WRITE_MODEM_STATUS) begin ft_miosi_out = {2'b00, reset_reply, 5'b00000}; ft_oe = 1'b1; end end end always_comb begin rx_write = 1'b0; tx_read = 1'b0; rx_wdata = ft_miosi_in; if (!ft_miso && phase[3]) begin case (state) STATE_STATUS: begin if (cmd == CMD_WRITE && !last_tx_failed) begin tx_read = 1'b1; end end STATE_DATA: begin if (cmd == CMD_READ) begin rx_write = 1'b1; end if (cmd == CMD_WRITE && !tx_empty) begin tx_read = 1'b1; end end endcase end end always_comb begin next_state = state; next_cmd = cmd; if (reset) begin next_state = STATE_IDLE; end else begin case (state) STATE_IDLE: begin if (ft_pwrsav) begin if (write_modem_status_pending) begin next_state = STATE_SELECT; next_cmd = CMD_WRITE_MODEM_STATUS; end else if (&modem_status_counter) begin next_state = STATE_SELECT; next_cmd = CMD_READ_MODEM_STATUS; end else if (!tx_empty || last_tx_failed) begin next_state = STATE_SELECT; next_cmd = CMD_WRITE; end else if (write_buffer_flush_pending) begin next_state = STATE_SELECT; next_cmd = CMD_WRITE_BUFFER_FLUSH; end else if (!rx_full) begin next_state = STATE_SELECT; next_cmd = CMD_READ; end end end STATE_SELECT: begin if (phase[3]) begin next_state = STATE_COMMAND; end end STATE_COMMAND: begin if (phase[3]) begin next_state = STATE_STATUS; end end STATE_STATUS: begin if (phase[3]) begin if (ft_miso) begin next_state = STATE_DESELECT; end else begin next_state = STATE_DATA; end end end STATE_DATA: begin if (phase[3]) begin if (ft_miso) begin next_state = STATE_DESELECT; end else if (cmd == CMD_READ) begin if (rx_almost_full) begin next_state = STATE_DESELECT; end end else if (cmd == CMD_WRITE) begin if (tx_empty) begin next_state = STATE_DESELECT; end end else begin next_state = STATE_DESELECT; end end end STATE_DESELECT: begin if (phase[1]) begin next_state = STATE_IDLE; end end default: begin next_state = STATE_IDLE; end endcase end end endmodule