`timescale 1 ns / 1 ps module ads1299_spi# ( parameter CLK_PERIOD = 10 , parameter CLK_DIV = 3 , // max: 255 parameter DATA_WIDTH = 8 , // max: 255 parameter NO_OF_SLAVES = 1 ) ( input clk , input rst_n , input [ 8-1:0] t_cs_n , input [ DATA_WIDTH-1:0] t_tdata , input t_tlast , output reg t_tready , input t_tvalid , output reg [ DATA_WIDTH-1:0] r_tdata , output reg r_tlast , output reg r_tvalid , output [NO_OF_SLAVES-1:0] cs , output sclk , output mosi , input miso ); //***************************************************************************** // localparam definition //***************************************************************************** localparam CNT_POS = CLK_DIV-1 ; localparam CNT_NEG = CLK_DIV*2-1 ; localparam T_SCCS = 2000/CLK_PERIOD ; localparam T_SCH = 600/CLK_PERIOD ; localparam IDLE = 4'd0; localparam TRANS_ST = 4'd1; localparam SCCS_ST = 4'd2; localparam CSH_ST = 4'd3; //***************************************************************************** // Internal register and wire declarations //***************************************************************************** reg [ 4-1:0] cstate ; reg [ 4-1:0] nstate ; reg [DATA_WIDTH-1:0] tx_data ; reg [DATA_WIDTH-1:0] rx_data ; reg clk_en ; reg [ 9-1:0] cnt ; reg sck_pos ; reg sck_neg ; reg [ 8-1:0] trans_cnt ; reg [ 8-1:0] slv_n ; reg [NO_OF_SLAVES-1:0] cs_r ; reg sclk_r ; reg [NO_OF_SLAVES-1:0] cs_release ; reg [ 8-1:0] t_sccs_cnt ; reg [ 8-1:0] t_sch_cnt ; reg read_flag ; //***************************************************************************** always@( posedge clk or negedge rst_n ) begin if( !rst_n ) cstate <= IDLE; else cstate <= nstate; end always@( * ) begin case ( cstate ) IDLE: if ( t_tvalid ) nstate <= TRANS_ST; else nstate <= IDLE; TRANS_ST: if( (trans_cnt == 0) && sck_neg ) nstate <= (cs_release[slv_n])? SCCS_ST : IDLE; else nstate <= TRANS_ST; SCCS_ST: if ( t_sccs_cnt == T_SCCS - 1 ) nstate <= CSH_ST; else nstate <= SCCS_ST; CSH_ST: if ( t_sch_cnt == T_SCH - 1 ) nstate <= IDLE; else nstate <= CSH_ST; default: nstate <= IDLE; endcase end always @ ( posedge clk or negedge rst_n ) begin if ( !rst_n ) t_tready <= 1'b0; else if ( nstate == IDLE ) t_tready <= 1'b1; else if ( cstate == IDLE && nstate == IDLE ) t_tready <= 1'b1; else t_tready <= 1'b0; end always @ ( posedge clk or negedge rst_n ) begin if ( !rst_n ) begin slv_n <= 0; end else if ( cstate == IDLE || nstate == IDLE ) begin slv_n <= t_cs_n; end end always @ ( posedge clk or negedge rst_n ) begin if( !rst_n ) cs_release <= 1'b0; else if ( t_tvalid && t_tready && t_tlast ) cs_release[slv_n] <= 1'b1; else if ( t_tvalid && t_tready ) cs_release[slv_n] <= 1'b0; end always @ ( posedge clk or negedge rst_n ) begin if( !rst_n ) cs_r <= {NO_OF_SLAVES{1'b1}}; else if ( cstate == TRANS_ST ) cs_r[slv_n] <= 1'b0; else if ( nstate == CSH_ST ) cs_r[slv_n] <= cs_release[slv_n]; end always @ ( posedge clk or negedge rst_n ) begin if ( !rst_n ) begin tx_data <= 0; end else if ( t_tready && t_tvalid ) begin tx_data <= t_tdata; end end always @ ( posedge clk or negedge rst_n ) begin if( !rst_n ) clk_en <= 0; else if ( ((cstate == IDLE) && (nstate == TRANS_ST)) || ((cstate == TRANS_ST) && (nstate != SCCS_ST)) ) clk_en <= 1'b1; else if ( nstate == SCCS_ST || nstate == IDLE ) clk_en <= 1'b0; end always @ ( posedge clk or negedge rst_n ) begin if( !rst_n ) cnt <= 0; else if ( !clk_en ) cnt <= 0; else if ( cnt == CLK_DIV*2-1 ) cnt <= 0; else cnt <= cnt + 1; end always @ ( posedge clk or negedge rst_n ) begin if( !rst_n ) sck_pos <= 0; else if ( clk_en && (cnt == CNT_POS) ) sck_pos <= 1; else sck_pos <= 0; end always @ ( posedge clk or negedge rst_n ) begin if( !rst_n ) sck_neg <= 0; else if ( clk_en && (cnt == CNT_NEG) ) sck_neg <= 1; else sck_neg <= 0; end always @ ( posedge clk or negedge rst_n ) begin if( !rst_n ) sclk_r <= 0; else if ( sck_pos && nstate == TRANS_ST ) sclk_r <= 1'b1; else if ( sck_neg ) sclk_r <= 1'b0; end always @ ( posedge clk or negedge rst_n ) begin if( !rst_n ) trans_cnt <= DATA_WIDTH; else if ( cstate == IDLE ) trans_cnt <= DATA_WIDTH; else if ( sck_pos && (trans_cnt != 0) ) trans_cnt <= trans_cnt - 1'b1; end always @ ( posedge clk or negedge rst_n ) begin if( !rst_n ) rx_data <= 0; else if ( sck_neg ) rx_data[trans_cnt] <= miso; end always @ ( posedge clk or negedge rst_n ) begin if( !rst_n ) read_flag <= 1'b0; else if ( cstate == TRANS_ST && nstate != TRANS_ST ) read_flag <= 1'b1; else read_flag <= 1'b0; end always @ ( posedge clk or negedge rst_n ) begin if( !rst_n ) begin r_tvalid <= 1'b0; r_tdata <= 0; r_tlast <= 1'b0; end else if ( read_flag ) begin r_tvalid <= 1'b1; r_tdata <= rx_data; r_tlast <= cs_release[slv_n]; end else begin r_tvalid <= 1'b0; r_tlast <= 1'b0; end end always @ ( posedge clk or negedge rst_n ) begin if( !rst_n ) begin t_sccs_cnt <= 0; end else if ( cstate != SCCS_ST ) begin t_sccs_cnt <= 0; end else begin t_sccs_cnt <= t_sccs_cnt + 1'b1; end end always @ ( posedge clk or negedge rst_n ) begin if( !rst_n ) begin t_sch_cnt <= 0; end else if ( cstate != CSH_ST ) begin t_sch_cnt <= 0; end else begin t_sch_cnt <= t_sch_cnt + 1'b1; end end //***************************************************************************** // Wire assignment //***************************************************************************** assign cs = cs_r; assign sclk = sclk_r; assign mosi = (trans_cnt==DATA_WIDTH)? 1'b0:tx_data[trans_cnt]; endmodule