URL
https://opencores.org/ocsvn/nysa_sata/nysa_sata/trunk
Subversion Repositories nysa_sata
[/] [nysa_sata/] [trunk/] [rtl/] [command/] [sata_command_layer.v] - Rev 2
Go to most recent revision | Compare with Previous | Blame | View Log
//sata_command_layer.v /* Distributed under the MIT license. Copyright (c) 2011 Dave McCoy (dave.mccoy@cospandesign.com) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ `include "sata_defines.v" `define RESET_TIMEOUT 32'h00000002 module sata_command_layer ( input rst, //reset input linkup, input clk, input data_in_clk, input data_in_clk_valid, input data_out_clk, input data_out_clk_valid, //User Interface output sata_init, output command_layer_ready, output reg busy, input send_sync_escape, input [15:0] user_features, //XXX: New Stb input write_data_en, input single_rdwr, input read_data_en, output dev_error, input send_user_command_stb, input soft_reset_en, output reg pio_data_ready, input [7:0] command, input [15:0] sector_count, input [47:0] sector_address, input [31:0] user_din, input user_din_stb, output [1:0] user_din_ready, input [1:0] user_din_activate, output [23:0] user_din_size, output [31:0] user_dout, output user_dout_ready, input user_dout_activate, input user_dout_stb, output [23:0] user_dout_size, //Transfer Layer Interface input transport_layer_ready, output reg sync_escape, output t_send_command_stb, output reg t_send_control_stb, output t_send_data_stb, input t_dma_activate_stb, input t_d2h_reg_stb, input t_pio_setup_stb, input t_d2h_data_stb, input t_dma_setup_stb, input t_set_device_bits_stb, input t_remote_abort, input t_xmit_error, input t_read_crc_error, //PIO input t_pio_response, input t_pio_direction, input [15:0] t_pio_transfer_count, input [7:0] t_pio_e_status, //Host to Device Register Values output [7:0] h2d_command, output reg [15:0] h2d_features, output [7:0] h2d_control, output [3:0] h2d_port_mult, output [7:0] h2d_device, output [47:0] h2d_lba, output [15:0] h2d_sector_count, //Device to Host Register Values input d2h_interrupt, input d2h_notification, input [3:0] d2h_port_mult, input [7:0] d2h_device, input [47:0] d2h_lba, input [15:0] d2h_sector_count, input [7:0] d2h_status, input [7:0] d2h_error, //command layer data interface input t_if_strobe, output [31:0] t_if_data, output t_if_ready, input t_if_activate, output [23:0] t_if_size, input t_of_strobe, input [31:0] t_of_data, output [1:0] t_of_ready, input [1:0] t_of_activate, output [23:0] t_of_size, //Debug output [3:0] cl_c_state, output [3:0] cl_w_state, output [3:0] cl_r_state ); //Parameters parameter IDLE = 4'h0; parameter PIO_WAIT_FOR_DATA = 4'h1; parameter PIO_WRITE_DATA = 4'h2; parameter WAIT_FOR_DATA = 4'h1; parameter WAIT_FOR_DMA_ACT = 4'h1; parameter WAIT_FOR_WRITE_DATA = 4'h2; parameter SEND_DATA = 4'h3; parameter WAIT_FOR_STATUS = 4'h4; //Registers/Wires reg [3:0] cntrl_state; reg srst; reg [7:0] status; wire idle; reg cntrl_send_data_stb; reg send_command_stb; reg prev_send_command; wire dev_busy; wire dev_data_req; reg [31:0] reset_count; wire reset_timeout; //Read State Machine reg [3:0] read_state; reg read_data_stb; reg single_read_prev; //Write State Machine reg [3:0] write_state; reg write_data_stb; reg single_write_prev; reg dma_send_data_stb; reg dma_act_detected; wire write_data_available; reg first_write; reg first_read; reg enable_tl_data_ready; //Ping Pong FIFOs wire [1:0] if_write_ready; wire [1:0] if_write_activate; wire [23:0] if_write_size; wire if_write_strobe; wire if_starved; wire [31:0] if_write_data; wire if_read_strobe; wire if_read_ready; wire if_read_activate; wire [23:0] if_read_size; wire [31:0] if_read_data; wire if_reset; wire [31:0] of_write_data; wire [1:0] of_write_ready; wire [1:0] of_write_activate; wire [23:0] of_read_size; wire of_write_strobe; wire out_fifo_starved; wire of_read_ready; wire [31:0] of_read_data; wire of_read_activate; wire [23:0] of_write_size; wire of_read_strobe; wire of_reset; //ping pong FIFO //Input FIFO ppfifo # ( .DATA_WIDTH (`DATA_SIZE ), .ADDRESS_WIDTH (`FIFO_ADDRESS_WIDTH ) ) fifo_in ( .reset (if_reset ), //XXX: Veify that new PPFIFO doesn't need an external reset //write side //XXX: This can be different clocks .write_clock (data_in_clk ), .write_data (if_write_data ), .write_ready (if_write_ready ), .write_activate (if_write_activate ), .write_fifo_size (if_write_size ), .write_strobe (if_write_strobe ), .starved (if_starved ), //read side //XXX: This can be different clocks .read_clock (clk ), .read_strobe (if_read_strobe ), .read_ready (if_read_ready ), .read_activate (if_read_activate ), .read_count (if_read_size ), .read_data (if_read_data ) ); //Output FIFO ppfifo # ( .DATA_WIDTH (`DATA_SIZE ), .ADDRESS_WIDTH (`FIFO_ADDRESS_WIDTH ) ) fifo_out ( .reset (of_reset ), //.reset (0), //write side //XXX: This can be different clocks .write_clock (clk ), .write_data (of_write_data ), .write_ready (of_write_ready ), .write_activate (of_write_activate ), .write_fifo_size (of_write_size ), .write_strobe (of_write_strobe ), .starved (out_fifo_starved ), //read side //XXX: This can be different clocks .read_clock (data_out_clk ), .read_strobe (of_read_strobe ), .read_ready (of_read_ready ), .read_activate (of_read_activate ), .read_count (of_read_size ), .read_data (of_read_data ) ); //Asynchronous Logic //Attach output of Input FIFO to TL assign t_if_ready = if_read_ready && enable_tl_data_ready; assign t_if_size = if_read_size; assign t_if_data = if_read_data; assign if_read_activate = t_if_activate; assign if_read_strobe = t_if_strobe; //Attach input of output FIFO to TL assign t_of_ready = of_write_ready; //assign t_of_size = of_write_size; assign t_of_size = 24'h00800; assign of_write_data = t_of_data; assign of_write_activate = t_of_activate; assign of_write_strobe = t_of_strobe; assign of_reset = (rst && data_out_clk_valid); assign if_reset = (rst && data_in_clk_valid); assign if_write_data = user_din; assign if_write_strobe = user_din_stb; assign user_din_ready = if_write_ready; assign if_write_activate = user_din_activate; //assign user_din_size = if_write_size; assign user_din_size = 24'h00800; //assign user_din_size = 24'h00400; //assign user_din_size = 24'h00200; assign user_dout = of_read_data; assign user_dout_ready = of_read_ready; assign of_read_activate = user_dout_activate; assign user_dout_size = of_read_size; assign of_read_strobe = user_dout_stb; assign write_data_available = (if_read_ready || if_read_activate) || (if_write_ready != 2'b11); //Strobes assign t_send_command_stb = read_data_stb || write_data_stb || send_command_stb; assign t_send_data_stb = dma_send_data_stb ||cntrl_send_data_stb; //IDLE assign idle = (cntrl_state == IDLE) && (read_state == IDLE) && (write_state == IDLE) && transport_layer_ready; assign command_layer_ready = idle; assign sata_init = reset_timeout; assign h2d_command = (write_data_en) ? `COMMAND_DMA_WRITE_EX : (read_data_en) ? `COMMAND_DMA_READ_EX : (send_user_command_stb) ? command : h2d_command; assign h2d_sector_count = sector_count; assign h2d_lba = (write_data_en) ? (!single_rdwr && !first_write) ? d2h_lba + 1 : sector_address : (read_data_en) ? (!single_rdwr && !first_read) ? d2h_lba + 1 : sector_address : sector_address; //XXX: The individual bits should be controlled directly assign h2d_control = {5'h00, srst, 2'b00}; //XXX: This should be controlled from a higher level assign h2d_port_mult = 4'h0; //XXX: This should be controlled from a higher level assign h2d_device = `D2H_REG_DEVICE; assign dev_busy = status[`STATUS_BUSY_BIT]; assign dev_data_req = status[`STATUS_DRQ_BIT]; assign dev_error = status[`STATUS_ERR_BIT]; assign cl_c_state = cntrl_state; assign cl_r_state = read_state; assign cl_w_state = write_state; assign reset_timeout = (reset_count >= `RESET_TIMEOUT); //Synchronous Logic //Control State Machine always @ (posedge clk) begin if (rst || (!linkup)) begin cntrl_state <= IDLE; h2d_features <= `D2H_REG_FEATURES; srst <= 0; //Strobes t_send_control_stb <= 0; cntrl_send_data_stb <= 0; pio_data_ready <= 0; status <= 0; prev_send_command <= 0; send_command_stb <= 0; reset_count <= 0; busy <= 1; end else begin t_send_control_stb <= 0; cntrl_send_data_stb <= 0; pio_data_ready <= 0; send_command_stb <= 0; //Reset Count if (reset_count < `RESET_TIMEOUT) begin reset_count <= reset_count + 1; end if (!reset_timeout) begin cntrl_state <= IDLE; end //detected the first a user attempting to send a command if (send_user_command_stb && !prev_send_command) begin prev_send_command <= 1; send_command_stb <= 1; end if (!send_user_command_stb) begin prev_send_command <= 0; end if (t_d2h_reg_stb) begin busy <= 0; h2d_features <= `D2H_REG_FEATURES; end if (t_send_command_stb || t_send_control_stb || send_user_command_stb) begin busy <= 1; if (send_user_command_stb) begin h2d_features <= user_features; end end case (cntrl_state) IDLE: begin //Soft Reset will break out of any flow if ((soft_reset_en) && !srst) begin srst <= 1; t_send_control_stb <= 1; reset_count <= 0; end if (idle) begin //The only way to transition to another state is if CL is IDLE //User Initiated commands if (!soft_reset_en && srst && reset_timeout) begin srst <= 0; t_send_control_stb <= 1; end end //Device Initiated Transfers if(t_pio_setup_stb) begin if (t_pio_direction) begin //Read from device cntrl_state <= PIO_WAIT_FOR_DATA; end else begin //Write to device cntrl_state <= PIO_WRITE_DATA; end end if (t_set_device_bits_stb) begin status <= d2h_status; //status register was updated end if (t_d2h_reg_stb) begin status <= d2h_status; end end PIO_WAIT_FOR_DATA: begin if (t_d2h_data_stb) begin //the next peice of data is related to the PIO pio_data_ready <= 1; cntrl_state <= IDLE; status <= t_pio_e_status; end end PIO_WRITE_DATA: begin if (if_read_activate) begin cntrl_send_data_stb <= 0; cntrl_state <= IDLE; status <= t_pio_e_status; end end default: begin cntrl_state <= IDLE; end endcase if (send_sync_escape) begin cntrl_state <= IDLE; busy <= 0; end end end //Read State Machine always @ (posedge clk) begin if (rst || (!linkup)) begin read_state <= IDLE; sync_escape <= 0; read_data_stb <= 0; single_read_prev <= 0; first_read <= 1; end else begin read_data_stb <= 0; sync_escape <= 0; if (!read_data_en) begin single_read_prev <= 0; end case (read_state) IDLE: begin if (idle) begin sync_escape <= 0; //The only way to transition to another state is if CL is IDLE if (read_data_en) begin if (single_rdwr) begin if (!single_read_prev) begin single_read_prev <= 1; read_data_stb <= 1; read_state <= WAIT_FOR_DATA; end end else begin //send a request to read data read_data_stb <= 1; read_state <= WAIT_FOR_DATA; end end else begin first_read <= 1; end end end WAIT_FOR_DATA: begin //This state seems useless because it only sets a value but the state is used to indicate the system is idle or not if (t_d2h_data_stb) begin first_read <= 0; end /* if (soft_reset_en) begin //XXX: Issue a SYNC ESCAPE to cancel a large read request otherwise let it play out //sync_escape <= 1; end */ end default: begin read_state <= IDLE; end endcase if (soft_reset_en || !reset_timeout || send_sync_escape) begin if (read_state != IDLE) begin sync_escape <= 1; end if (send_sync_escape) begin sync_escape <= 1; end read_state <= IDLE; end //If this is received go back to IDLE if (t_d2h_reg_stb) begin read_state <= IDLE; end end end //Write State Machine always @ (posedge clk) begin if (rst || (!linkup)) begin write_state <= IDLE; dma_send_data_stb <= 0; write_data_stb <= 0; single_write_prev <= 0; first_write <= 1; enable_tl_data_ready <= 0; dma_act_detected <= 0; end else begin dma_send_data_stb <= 0; write_data_stb <= 0; if (enable_tl_data_ready && if_read_activate) begin //Closes the loop on the data write feedback enable_tl_data_ready <= 0; end if (!write_data_en) begin single_write_prev <= 0; end if (t_dma_activate_stb) begin //Set an enable signal instead of a strobe so that there is no chance of missing this signal dma_act_detected <= 1; end case (write_state) IDLE: begin if (idle) begin //The only way to transition to another state is if CL is IDLE if (write_data_en) begin if (single_rdwr) begin if (!single_write_prev) begin single_write_prev <= 1; write_state <= WAIT_FOR_DMA_ACT; write_data_stb <= 1; end end else begin //send a request to write data write_state <= WAIT_FOR_DMA_ACT; write_data_stb <= 1; end end else begin //reset the the first write when the user deassertes the write_data_en first_write <= 1; end end end WAIT_FOR_DMA_ACT: begin if (dma_act_detected) begin dma_act_detected <= 0; first_write <= 0; enable_tl_data_ready <= 1; write_state <= WAIT_FOR_WRITE_DATA; end end WAIT_FOR_WRITE_DATA: begin if (if_read_activate) begin write_state <= SEND_DATA; end end SEND_DATA: begin if (transport_layer_ready) begin //Send the Data FIS dma_send_data_stb <= 1; write_state <= WAIT_FOR_DMA_ACT; end end WAIT_FOR_STATUS: begin if (t_d2h_reg_stb) begin write_state <= IDLE; end end default: begin write_state <= IDLE; end endcase if (soft_reset_en || !reset_timeout) begin //Break out of the normal flow and return to IDLE write_state <= IDLE; end if (t_d2h_reg_stb) begin //Whenever I read a register transfer from the device I need to go back to IDLE write_state <= IDLE; end if (send_sync_escape) begin write_state <= IDLE; end end end endmodule
Go to most recent revision | Compare with Previous | Blame | View Log