| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273 |
- //////////////////////////////////////////////////////////////////////////////////
- // Company : NPK TAIR
- // Engineer : Yuri Donskoy
- //
- // Create Date (dd/mm/yyyy) :
- // Design Name :
- // Module Name :
- // Project Name :
- // Target Devices :
- // Tool versions :
- // Description :
- //
- // Dependencies :
- //
- // Revision : 1.0 - It only send data (no miso port)
- // Additional Comments : MISO port need to be add. What about multiple slave select?
- //
- //////////////////////////////////////////////////////////////////////////////////
- module SpiMaster (
- clk_i,
- rst_i,
- data_i,
- valid_i,
- ready_o,
- mosi_o,
- sck_o,
- ss_o
- );
- //================================================================================
- //
- // FUNCTIONS
- //
- //================================================================================
- function integer bit_num;
- input integer value;
- begin
- bit_num = 0;
- while (value > 0) begin
- value = value >> 1;
- bit_num = bit_num + 1;
- end
- end
- endfunction
- //================================================================================
- //
- // PARAMETER/LOCALPARAM
- //
- //================================================================================
- parameter CLK_DIVISOR_POWER = 4; //WAS 2 !! DONT FORGET TO CHANGE!
- parameter DATA_WIDTH = 24;
- parameter CPOL = 0;
- parameter CPHA = 0;
- parameter DATA_DIRECTION = "MSBT"; // MSB or LSB
- parameter EN_START_DELAY = "NO"; // YES or NO
- localparam BIT_CNT_W = bit_num(DATA_WIDTH);
- //================================================================================
- //
- // STATE MACHINE STATES
- //
- //================================================================================
- localparam SM_IDLE_S = 2'b00;
- localparam SM_START_S = 2'b01;
- localparam SM_DATA_S = 2'b10;
- localparam SM_STOP_S = 2'b11;
- //================================================================================
- //
- // PORTS
- //
- //================================================================================
- input clk_i;
- input rst_i;
- input [DATA_WIDTH-1:0] data_i;
- input valid_i;
- output ready_o;
- output reg mosi_o;
- output reg sck_o;
- output reg ss_o;
- //================================================================================
- //
- // REG/WIRE
- //
- //================================================================================
- reg [1:0] sm_curr_state;
- reg [1:0] sm_next_state;
- reg sm_clk_div_en;
- // Clock divider outputs
- wire clk_divider_redge;
- wire clk_divider_fedge;
- // Bits counter
- reg [BIT_CNT_W-1:0] bit_cnt_r;
- reg [BIT_CNT_W-1:0] bit_cnt_next;
- // Data buffers
- reg [DATA_WIDTH-1:0] tx_buffer_r;
- reg [DATA_WIDTH-1:0] tx_buffer_next;
- wire [DATA_WIDTH-1:0] tx_buffer_shifted;
- wire tx_curr_bit;
- // Output data next
- reg mosi_next;
- reg sck_next;
- reg ss_next;
- // Edges
- wire mosi_shift_edge;
- wire ss_start_edge;
- wire ss_stop_edge;
- wire sck_leading_edge;
- wire sck_trailing_edge;
- //================================================================================
- //
- // INTEGER/GENVAR
- //
- //================================================================================
- //================================================================================
- //
- // ASSIGN
- //
- //================================================================================
- assign mosi_shift_edge = (CPHA[0] == 1'b1) && (EN_START_DELAY != "YES") || (CPHA[0] == 1'b0) && (EN_START_DELAY == "YES") ? clk_divider_fedge : clk_divider_redge;
- assign ss_start_edge = (EN_START_DELAY == "YES") ? clk_divider_fedge : clk_divider_redge;
- assign ss_stop_edge = (EN_START_DELAY == "YES") ? clk_divider_redge : clk_divider_fedge;
- assign sck_leading_edge = (EN_START_DELAY == "YES") ? clk_divider_redge : clk_divider_fedge;
- assign sck_trailing_edge = (EN_START_DELAY == "YES") ? clk_divider_fedge : clk_divider_redge;
- assign tx_buffer_shifted = (DATA_DIRECTION == "MSB") ? tx_buffer_r << 1 : tx_buffer_r >> 1;
- assign tx_curr_bit = (DATA_DIRECTION == "MSB") ? tx_buffer_r[DATA_WIDTH-1] : tx_buffer_r[0];
- assign ready_o = sm_curr_state == SM_IDLE_S;
- //================================================================================
- //
- // CODING
- //
- //================================================================================
- // Sequential logic
- always @(posedge clk_i or posedge rst_i) begin
- if (rst_i) begin
- sm_curr_state <= 0;
- tx_buffer_r <= {DATA_WIDTH{1'b0}};
- bit_cnt_r <= {BIT_CNT_W{1'b0}};
- mosi_o <= 1'b0;
- sck_o <= CPOL[0];
- ss_o <= 1'b1;
- end else begin
- sm_curr_state <= sm_next_state;
- tx_buffer_r <= tx_buffer_next;
- bit_cnt_r <= bit_cnt_next;
- mosi_o <= mosi_next;
- sck_o <= sck_next;
- ss_o <= ss_next;
- end
- end
- // Combinational logic
- always @(*) begin
- sm_next_state = SM_IDLE_S;
- tx_buffer_next = tx_buffer_r;
- mosi_next = mosi_o;
- sck_next = sck_o;
- ss_next = ss_o;
- sm_clk_div_en = 1'b1;
- bit_cnt_next = bit_cnt_r;
- case(sm_curr_state)
- SM_IDLE_S : begin
- if (valid_i) begin
- sm_next_state = SM_START_S;
- end else begin
- sm_next_state = SM_IDLE_S;
- end
- tx_buffer_next = data_i;
- sm_clk_div_en = 1'b0;
- bit_cnt_next = {BIT_CNT_W{1'b0}};
- end
- SM_START_S : begin
- if (ss_start_edge) begin
- sm_next_state = SM_DATA_S;
- ss_next = 1'b0;
- if (!CPHA[0]) begin
- mosi_next = tx_curr_bit;
- tx_buffer_next = tx_buffer_shifted;
- bit_cnt_next = bit_cnt_r + {{(BIT_CNT_W-1){1'b0}}, 1'b1};
- end
- end else begin
- sm_next_state = SM_START_S;
- end
- end
- SM_DATA_S : begin
- sm_next_state = SM_DATA_S;
- if (sck_leading_edge) begin
- sck_next = ~CPOL[0];
- end
- if (sck_trailing_edge) begin
- sck_next = CPOL[0];
- if (bit_cnt_r == DATA_WIDTH[BIT_CNT_W-1:0]) begin
- sm_next_state = SM_STOP_S;
- end
- end
- if (mosi_shift_edge) begin
- mosi_next = tx_curr_bit;
- tx_buffer_next = tx_buffer_shifted;
- bit_cnt_next = bit_cnt_r + {{(BIT_CNT_W-1){1'b0}}, 1'b1};
- end
- end
- SM_STOP_S : begin
- if (ss_stop_edge) begin
- if (CPHA[0]) begin
- mosi_next = tx_curr_bit;
- end
- sm_next_state = SM_IDLE_S;
- ss_next = 1'b1;
- end else begin
- sm_next_state = SM_STOP_S;
- end
- end
- endcase
- end
- // Clock divider
- Power2ClkDivider #(
- .DIVISOR_POWER (CLK_DIVISOR_POWER)
- ) ClkDividerInst (
- .clk_i (clk_i),
- .rst_i (rst_i),
- .valid_i (sm_clk_div_en),
- .signal_o (),
- .rising_edge_o (clk_divider_redge),
- .falling_edge_o (clk_divider_fedge)
- );
- endmodule
|