URL
https://opencores.org/ocsvn/attiny_atmega_xmega_core/attiny_atmega_xmega_core/trunk
Subversion Repositories attiny_atmega_xmega_core
[/] [attiny_atmega_xmega_core/] [trunk/] [rtl/] [io/] [spi_s.v] - Rev 15
Compare with Previous | Blame | View Log
/* * This IP is a SPI IO adapter implementation. * * Copyright (C) 2018 Iulian Gheorghiu * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ `timescale 1ns / 1ps `include "io_s_h.v" module spi_s #( parameter DINAMIC_BAUDRATE = "TRUE", parameter BAUDRATE_DIVIDER = 3, parameter ADDRESS = 0, parameter BUS_ADDR_DATA_LEN = 16 )( input rst, input clk, input [BUS_ADDR_DATA_LEN-1:0]addr, input wr, input rd, input [7:0]bus_in, output reg[7:0]bus_out, output int, output sck,/* SPI 'sck' signal (output) */ output mosi,/* SPI 'mosi' signal (output) */ input miso,/* SPI 'miso' signal (input) */ output reg ss/* SPI 'ss' signal (if send buffer is maintained full the ss signal will not go high between between transmit chars)(output) */ ); //reg [7:0]CTRL; //reg [7:0]BAUD; wire cs_int = addr >= ADDRESS && addr < (ADDRESS + 8); wire rd_int = cs_int && rd; wire wr_int = cs_int && wr; reg [7:0]baud_cnt; wire buffempty; reg [7:0]CTRL; reg [7:0]INTCTRL; reg [7:0]STATUS; localparam WORD_LEN = 8; localparam PRESCALLER_SIZE = 8; reg _mosi; reg charreceivedp; reg charreceivedn; reg inbufffullp = 1'b0; reg inbufffulln = 1'b0; reg [WORD_LEN - 1:0]input_buffer; reg [WORD_LEN - 1:0]output_buffer; assign buffempty = ~(inbufffullp ^ inbufffulln); reg [2:0]prescallerbuff; always @ (posedge clk or posedge rst) begin if(rst) begin inbufffullp <= 1'b0; prescallerbuff <= 3'b000; CTRL <= 0; INTCTRL <= 0; input_buffer <= 0; end else begin if(wr_int) begin case(addr[2:0]) `SPI_CTRL: CTRL <= bus_in; `SPI_INTCTRL: INTCTRL <= bus_in; `SPI_DATA: begin if(inbufffullp == inbufffulln && buffempty && CTRL[`SPI_ENABLE_bp]) begin inbufffullp <= ~inbufffullp; prescallerbuff <= {CTRL[`SPI_CLK2X_bp], CTRL[`SPI_PRESCALER_gp + 1:`SPI_PRESCALER_gp]}; input_buffer <= bus_in; end end endcase end end end always @ (posedge clk or posedge rst) begin if(rst) begin STATUS <= 8'h00; charreceivedn <= 1'b0; end else if(rd_int) begin case(addr[2:0]) `SPI_DATA: STATUS[`SPI_IF_bp] <= 1'b0; endcase end else if(charreceivedp != charreceivedn) begin STATUS[`SPI_IF_bp] <= 1'b1; charreceivedn <= ~charreceivedn; end end always @ (*) begin bus_out <= 8'bz; if(rd_int) begin case(addr[2:0]) `SPI_CTRL: bus_out <= CTRL; `SPI_INTCTRL: bus_out <= INTCTRL; `SPI_STATUS: bus_out <= STATUS; `SPI_DATA: bus_out <= output_buffer; endcase end end assign int = INTCTRL[`SPI_INTLVL_gp + 1: `SPI_INTLVL_gp] ? STATUS[`SPI_IF_bp] : 1'b0; /***********************************************/ /************ !Asynchronus send ****************/ /***********************************************/ localparam state_idle = 1'b0; localparam state_busy = 1'b1; reg state; reg [PRESCALLER_SIZE - 1:0]prescaller_cnt; reg [WORD_LEN - 1:0]shift_reg_out; reg [WORD_LEN - 1:0]shift_reg_in; reg [4:0]sckint; //reg sckintn; reg [2:0]prescallerint; reg [7:0]prescdemux; always @ (*) begin case(prescallerint) 3'b000: prescdemux <= 3; 3'b001: prescdemux <= 15; 3'b010: prescdemux <= 63; 3'b011: prescdemux <= 127; 3'b100: prescdemux <= 1; 3'b101: prescdemux <= 7; 3'b110: prescdemux <= 31; 3'b111: prescdemux <= 63; endcase end reg lsbfirstint; reg [1:0]modeint; always @ (posedge clk or posedge rst) begin if(rst) begin baud_cnt = 'h00; inbufffulln <= 1'b0; ss <= 1'b1; state <= state_idle; prescaller_cnt <= {PRESCALLER_SIZE{1'b0}}; prescallerint <= {PRESCALLER_SIZE{3'b0}}; shift_reg_out <= {WORD_LEN{1'b0}}; shift_reg_in <= {WORD_LEN{1'b0}}; sckint <= {5{1'b0}}; _mosi <= 1'b1; output_buffer <= {WORD_LEN{1'b0}}; charreceivedp <= 1'b0; lsbfirstint <= 1'b0; modeint <= 2'b00; end else begin if(CTRL[`SPI_ENABLE_bp]) begin if(DINAMIC_BAUDRATE == "TRUE" ? baud_cnt == prescdemux : baud_cnt == {BAUDRATE_DIVIDER}) begin baud_cnt <= 'h00; case(state) state_idle: begin if(inbufffullp != inbufffulln) begin inbufffulln <= ~inbufffulln; ss <= 1'b0; prescaller_cnt <= {PRESCALLER_SIZE{1'b0}}; prescallerint <= prescallerbuff; lsbfirstint <= CTRL[`SPI_DORD_bp]; modeint <= CTRL[`SPI_MODE_gp + 1:`SPI_MODE_gp]; shift_reg_out <= input_buffer; state <= state_busy; if(!CTRL[`SPI_MODE_gp]) begin if(!CTRL[`SPI_DORD_bp]) _mosi <= input_buffer[WORD_LEN - 1]; else _mosi <= input_buffer[0]; end end end state_busy: begin if(prescaller_cnt != prescdemux) begin prescaller_cnt <= prescaller_cnt + 1; end else begin prescaller_cnt <= {PRESCALLER_SIZE{1'b0}}; sckint <= sckint + 1; if(sckint[0] == modeint[0]) begin if(!lsbfirstint) begin shift_reg_in <= {miso, shift_reg_in[7:1]}; shift_reg_out <= {shift_reg_out[6:0], 1'b1}; end else begin shift_reg_in <= {shift_reg_in[6:0], miso}; shift_reg_out <= {1'b1, shift_reg_out[7:1]}; end end else begin if(sckint[4:1] == WORD_LEN - 1) begin sckint <= {5{1'b0}}; if(inbufffullp == inbufffulln) begin ss <= 1'b1; end output_buffer <= shift_reg_in; if(charreceivedp == charreceivedn) begin charreceivedp <= ~charreceivedp; end state <= state_idle; end else begin if(!lsbfirstint) _mosi <= shift_reg_out[WORD_LEN - 1]; else _mosi <= shift_reg_out[0]; end end end end endcase end else begin baud_cnt <= baud_cnt + 1; end end end end assign sck = (modeint[1])? ~sckint : sckint; assign mosi = (ss) ? 1'b1:_mosi; endmodule