From ebd8acd740f0ebdf53c151b1bcac844dd97c0dce Mon Sep 17 00:00:00 2001 From: Matteo Suppo Date: Wed, 26 Jun 2019 12:14:00 +0200 Subject: [PATCH] First commit+ --- TSPI.json | 60 +++++++++ arduino/VidorSPI/VidorSPI.cpp | 243 ++++++++++++++++++++++++++++++++++ arduino/VidorSPI/VidorSPI.h | 148 +++++++++++++++++++++ doc/TSPI_RPC.adoc | 44 ++++++ rtl/tiny_spi.v | 233 ++++++++++++++++++++++++++++++++ rtl/tiny_spi_hw.tcl | 158 ++++++++++++++++++++++ softcore/inc/tspi.h | 57 ++++++++ softcore/src/tspi.c | 204 ++++++++++++++++++++++++++++ softcore/tiny_spi_sw.tcl | 53 ++++++++ 9 files changed, 1200 insertions(+) create mode 100644 TSPI.json create mode 100644 arduino/VidorSPI/VidorSPI.cpp create mode 100644 arduino/VidorSPI/VidorSPI.h create mode 100644 doc/TSPI_RPC.adoc create mode 100644 rtl/tiny_spi.v create mode 100644 rtl/tiny_spi_hw.tcl create mode 100644 softcore/inc/tspi.h create mode 100644 softcore/src/tspi.c create mode 100644 softcore/tiny_spi_sw.tcl diff --git a/TSPI.json b/TSPI.json new file mode 100644 index 0000000..c71db87 --- /dev/null +++ b/TSPI.json @@ -0,0 +1,60 @@ +{ + "sc":{ + "name":"TSPI", + "include":"tspi.h", + "uid":"TSPI_UID", + "rpc":"tspiRpc", + "ver":"TSPI_VER", + "loop":"", + "priv":[ + { + "type":"d", + "device":{ + "func":"TSPI_DEV" + } + } + ] + }, + "module":{ + "name":"tiny_spi", + "version":"1.0", + "className":"VidorSPI", + "instanceName":"SPI", + "description":"SPI", + "author":"", + "parameters":[ + {"name":"BAUD_WIDTH", "def":"8"}, + {"name":"BAUD_DIV", "def":"0"}, + {"name":"SPI_MODE", "def":"0"} + ] + }, + "interfaces":[ + { + "name":"spi", + "type":"output", + "mates":["pins"], + "visibility":"1", + "signals": [ + {"name":"MISO", "bits":"1", "direction":"input" , "fid":"TSPI_PIN_FNC_MISO"}, + {"name":"MOSI", "bits":"1", "direction":"output", "fid":"TSPI_PIN_FNC_MOSI"}, + {"name":"SCLK", "bits":"1", "direction":"output", "fid":"TSPI_PIN_FNC_CLK" }, + {"name":"CS" , "bits":"1", "direction":"output", "fid":"TSPI_PIN_FNC_SS" } + ] + } + ], + "hw":[ + { + "interface":"avalon_slave", + "name":"avalon_slave", + "parameters":[ + {"signal":"address", "name":"SPAN", "value":"3"}, + {"signal":"read_data", "name":"BIT_WIDTH", "value":"32"}, + {"signal":"write_data", "name":"BIT_WIDTH", "value":"32"} + ] + }, + { + "interface":"spi", + "name":"spi" + } + ] +} diff --git a/arduino/VidorSPI/VidorSPI.cpp b/arduino/VidorSPI/VidorSPI.cpp new file mode 100644 index 0000000..407a3bf --- /dev/null +++ b/arduino/VidorSPI/VidorSPI.cpp @@ -0,0 +1,243 @@ +/* + * SPI Master library for Arduino Zero. + * Copyright (c) 2015 Arduino LLC + * Copyright (c) 2018 Arduino SA. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "VidorSPI.h" +#include "VidorIO.h" +#include +#include +#include + +#define SPI_IMODE_NONE 0 +#define SPI_IMODE_EXTINT 1 +#define SPI_IMODE_GLOBAL 2 + +#define MAX_CLOCK_FREQUENCY 50000000 //50MHz + +VidorSPIClass::VidorSPIClass(int _mosi, int _miso, int _sck, int _cs) : settings(SPISettings(0, MSBFIRST, SPI_MODE0)) +{ + initialized = false; + mosi = _mosi; + miso = _miso; + sck = _sck; + cs = _cs; +} + +int VidorSPIClass::begin() +{ + int ret; + if (cs == -1) { + ret = init(TSPI_UID, digital_to_fpga(mosi), digital_to_fpga(miso), digital_to_fpga(sck)); + } else { + ret = init(TSPI_UID, digital_to_fpga(mosi), digital_to_fpga(miso), digital_to_fpga(sck), digital_to_fpga(cs)); + } + if (ret < 0) { + return -1; + } + + uint32_t rpc[1]; + rpc[0] = RPC_CMD(info.giid, info.chn, 2); + VidorMailbox.sendCommand(rpc, 1); + + config(DEFAULT_SPI_SETTINGS); +} + +void VidorSPIClass::setSPIMode(int baud, int mode, int bitOrder) +{ + uint32_t rpc[5]; + rpc[0] = RPC_CMD(info.giid, info.chn, 5); + rpc[1] = baud; + rpc[2] = mode; + rpc[3] = bitOrder; + rpc[4] = false; // automatic SS + VidorMailbox.sendCommand(rpc, 5); +} + +void VidorSPIClass::config(SPISettings settings) +{ + if (this->settings != settings) { + this->settings.clockFreq = settings.getClockFreq(); + this->settings.dataMode = settings.getDataMode(); + this->settings.bitOrder = settings.getBitOrder(); + setSPIMode(this->settings.clockFreq, this->settings.dataMode, this->settings.bitOrder); + } +} + +void VidorSPIClass::end() +{ + uint32_t rpc[1]; + + rpc[0] = RPC_CMD(info.giid, info.chn, 4); + VidorMailbox.sendCommand(rpc, 1); + initialized = false; +} + +#ifndef interruptsStatus +#define interruptsStatus() __interruptsStatus() +static inline unsigned char __interruptsStatus(void) __attribute__((always_inline, unused)); +static inline unsigned char __interruptsStatus(void) +{ + // See http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0497a/CHDBIBGJ.html + return (__get_PRIMASK() ? 0 : 1); +} +#endif + +void VidorSPIClass::usingInterrupt(int interruptNumber) +{ + if ((interruptNumber == NOT_AN_INTERRUPT) || (interruptNumber == EXTERNAL_INT_NMI)) + return; + + uint8_t irestore = interruptsStatus(); + noInterrupts(); + + if (interruptNumber >= EXTERNAL_NUM_INTERRUPTS) + interruptMode = SPI_IMODE_GLOBAL; + else + { + interruptMode |= SPI_IMODE_EXTINT; + interruptMask |= (1 << interruptNumber); + } + + if (irestore) + interrupts(); +} + +void VidorSPIClass::notUsingInterrupt(int interruptNumber) +{ + if ((interruptNumber == NOT_AN_INTERRUPT) || (interruptNumber == EXTERNAL_INT_NMI)) + return; + + if (interruptMode & SPI_IMODE_GLOBAL) + return; // can't go back, as there is no reference count + + uint8_t irestore = interruptsStatus(); + noInterrupts(); + + interruptMask &= ~(1 << interruptNumber); + + if (interruptMask == 0) + interruptMode = SPI_IMODE_NONE; + + if (irestore) + interrupts(); +} + +void VidorSPIClass::beginTransaction(SPISettings settings) +{ + if (interruptMode != SPI_IMODE_NONE) + { + if (interruptMode & SPI_IMODE_GLOBAL) + { + interruptSave = interruptsStatus(); + noInterrupts(); + } + else if (interruptMode & SPI_IMODE_EXTINT) + EIC->INTENCLR.reg = EIC_INTENCLR_EXTINT(interruptMask); + } + + + config(settings); +} + +void VidorSPIClass::endTransaction(void) +{ + if (interruptMode != SPI_IMODE_NONE) + { + if (interruptMode & SPI_IMODE_GLOBAL) + { + if (interruptSave) + interrupts(); + } + else if (interruptMode & SPI_IMODE_EXTINT) + EIC->INTENSET.reg = EIC_INTENSET_EXTINT(interruptMask); + } +} + +void VidorSPIClass::setBitOrder(BitOrder order) +{ + settings.bitOrder = order; + setSPIMode(settings.clockFreq, settings.dataMode, settings.bitOrder); +} + +void VidorSPIClass::setDataMode(uint8_t mode) +{ + settings.dataMode = mode; + setSPIMode(settings.clockFreq, settings.dataMode, settings.bitOrder); +} + +void VidorSPIClass::setClockDivider(uint8_t div) +{ + settings.clockFreq = MAX_CLOCK_FREQUENCY / div; + setSPIMode(settings.clockFreq, settings.dataMode, settings.bitOrder); +} + +byte VidorSPIClass::transfer(uint8_t data) +{ + transfer(&data, 1); + return data; +} + +uint16_t VidorSPIClass::transfer16(uint16_t data) { + union { uint16_t val; struct { uint8_t lsb; uint8_t msb; }; } t; + + t.val = data; + + if (settings.bitOrder == LSBFIRST) { + t.lsb = transfer(t.lsb); + t.msb = transfer(t.msb); + } else { + t.msb = transfer(t.msb); + t.lsb = transfer(t.lsb); + } + + return t.val; +} + +void VidorSPIClass::transfer(void *buf, size_t count) +{ + //VidorIO::transferDataSPI(index, (uint8_t*)buf, count); + uint32_t rpc[256]; + if (count > 128) { + return; + } + rpc[0] = RPC_CMD(info.giid, info.chn, 6); + rpc[1] = count; + rpc[2] = 0; + memcpy(&rpc[2], buf, count); + int ret = VidorMailbox.sendCommand(rpc, 2+(rpc[1]+3)/4); + + VidorMailbox.read(2, &rpc[2], (count+3)/4); + memcpy(buf, &rpc[2], count); +} + +void VidorSPIClass::attachInterrupt() { + // Should be enableInterrupt() +} + +void VidorSPIClass::detachInterrupt() { + // Should be disableInterrupt() +} + +VidorSPIClass SPIFPGA0 (A2, A6, A3, A4); +VidorSPIClass SPIFPGA1 (A5, 0, 1, 2); +VidorSPIClass SPIFPGA2 (3, 7, 4, 5); +VidorSPIClass SPIFPGA3 (6, 8, 9, 10); +VidorSPIClass SPIFPGA4 (11, 12, 13, 14); + +VidorSPIClass SPIEx ((32*2)+19, (32*2)+20, (32*2)+18, -1); // Internally connected to NiNa module diff --git a/arduino/VidorSPI/VidorSPI.h b/arduino/VidorSPI/VidorSPI.h new file mode 100644 index 0000000..6901c6a --- /dev/null +++ b/arduino/VidorSPI/VidorSPI.h @@ -0,0 +1,148 @@ +/* + * SPI Master library for Arduino Zero. + * Copyright (c) 2015 Arduino LLC + * Copyright (c) 2018 Arduino SA. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _VIDORSPI_H_INCLUDED +#define _VIDORSPI_H_INCLUDED + +#include +#include +#include "defines.h" +#include "VidorIO.h" + +class VidorSPISettings { + public: + VidorSPISettings(uint32_t clock, BitOrder bitOrder, uint8_t dataMode) { + if (__builtin_constant_p(clock)) { + init_AlwaysInline(clock, bitOrder, dataMode); + } else { + init_MightInline(clock, bitOrder, dataMode); + } + } + + // Default speed set to 4MHz, SPI mode set to MODE 0 and Bit order set to MSB first. + VidorSPISettings() { init_AlwaysInline(4000000, MSBFIRST, SPI_MODE0); } + + VidorSPISettings(SPISettings& settings) { + init_MightInline(settings.getClockFreq(), settings.getBitOrder(), settings.getDataMode()); + } + + VidorSPISettings(SPISettings settings) { + init_MightInline(settings.getClockFreq(), settings.getBitOrder(), settings.getDataMode()); + } + + bool operator==(const VidorSPISettings& rhs) const + { + if ((this->clockFreq == rhs.clockFreq) && + (this->bitOrder == rhs.bitOrder) && + (this->dataMode == rhs.dataMode)) { + return true; + } + return false; + } + + bool operator!=(const VidorSPISettings& rhs) const + { + return !(*this == rhs); + } + + bool operator==(const SPISettings& rhs) const + { + if ((this->clockFreq == rhs.getClockFreq()) && + (this->bitOrder == rhs.getBitOrder()) && + (this->dataMode == rhs.getDataMode())) { + return true; + } + return false; + } + + bool operator!=(const SPISettings& rhs) const + { + return !(*this == rhs); + } + + private: + void init_MightInline(uint32_t clock, BitOrder bitOrder, uint8_t dataMode) { + init_AlwaysInline(clock, bitOrder, dataMode); + } + + void init_AlwaysInline(uint32_t clock, BitOrder bitOrder, uint8_t dataMode) __attribute__((__always_inline__)) { + this->clockFreq = clock; + this->bitOrder = bitOrder; + this->dataMode = dataMode; + } + + uint32_t clockFreq; + uint8_t dataMode; + BitOrder bitOrder; + + friend class VidorSPIClass; + friend class BitBangedSPI; +}; + + +class VidorSPIClass : public VidorIP { + public: + VidorSPIClass(int _mosi,int _miso,int _sck,int _cs); + + byte transfer(uint8_t data); + uint16_t transfer16(uint16_t data); + void transfer(void *buf, size_t count); + + void setSPIMode(int baud, int mode, int bitOrder); + + // Transaction Functions + void usingInterrupt(int interruptNumber); + void notUsingInterrupt(int interruptNumber); + void beginTransaction(SPISettings settings); + void endTransaction(void); + + // SPI Configuration methods + void attachInterrupt(); + void detachInterrupt(); + + int begin(); + void end(); + + void setBitOrder(BitOrder order); + void setDataMode(uint8_t uc_mode); + void setClockDivider(uint8_t uc_div); + + private: + void config(SPISettings settings); + + VidorSPISettings settings; + int mosi; + int miso; + int sck; + int cs; + bool initialized; + uint8_t interruptMode; + char interruptSave; + uint32_t interruptMask; +}; + +extern VidorSPIClass SPIFPGA0; +extern VidorSPIClass SPIFPGA1; +extern VidorSPIClass SPIFPGA2; +extern VidorSPIClass SPIFPGA3; +extern VidorSPIClass SPIFPGA4; +extern VidorSPIClass SPIEx; + +#endif diff --git a/doc/TSPI_RPC.adoc b/doc/TSPI_RPC.adoc new file mode 100644 index 0000000..679673e --- /dev/null +++ b/doc/TSPI_RPC.adoc @@ -0,0 +1,44 @@ += TSPI IP RPC + +.Module Definition +[%autowidth] +|===================================================================================================== +|Type | driver module +|UID | 0xEBCE5 +|IP Version | 0.0 +|DRV Version | 0.0 +|Channel | unused +|===================================================================================================== + +== Commands +.Commands +[%autowidth] +|===================================================================================================== +^|Cmd ^|Description ^|Parameters ^|Return + +^|2 |Setup |none |RetCod +^|4 |End |none |RetCod +^|5 |tspiModeSet |Baud, mode, bit_order, ss_auto |RetCod +^|6 |tspiTrx |Len, Buffer |RetCod, len, data read +^|7 |tspiByte |byte |Byte read + +|===================================================================================================== + +=== tspiModeSet +Set spi mode. +.Parameters +[%autowidth] +|===================================================================================================== +^|Parameter ^|Description + +|Baud |SPI speed +|mode |SPI mode, can be 0, 1, 2 or 3. +|bit_order |not used +|ss_auto |Set SS manual or auto handling +|===================================================================================================== + +=== tspiTrx +Start len byte transaction, return bytes read from SPI. + +=== tspiByte +Start one byte transaction, return byte read from SPI. diff --git a/rtl/tiny_spi.v b/rtl/tiny_spi.v new file mode 100644 index 0000000..929114b --- /dev/null +++ b/rtl/tiny_spi.v @@ -0,0 +1,233 @@ +////////////////////////////////////////////////////////////////////// +//// //// +//// tiny_spi.v //// +//// //// +//// This file is part of the TINY SPI IP core project //// +//// http://www.opencores.org/projects/tiny_spi/ //// +//// //// +//// Author(s): //// +//// - Thomas Chou //// +//// //// +//// All additional information is avaliable in the README //// +//// file. //// +//// //// +////////////////////////////////////////////////////////////////////// +//// //// +//// Copyright (C) 2010 Authors //// +//// //// +//// This source file may be used and distributed without //// +//// restriction provided that this copyright statement is not //// +//// removed from the file and that any derivative work contains //// +//// the original copyright notice and the associated disclaimer. //// +//// //// +//// This source file is free software; you can redistribute it //// +//// and/or modify it under the terms of the GNU Lesser General //// +//// Public License as published by the Free Software Foundation; //// +//// either version 2.1 of the License, or (at your option) any //// +//// later version. //// +//// //// +//// This source 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 Lesser General Public License for more //// +//// details. //// +//// //// +//// You should have received a copy of the GNU Lesser General //// +//// Public License along with this source; if not, download it //// +//// from http://www.opencores.org/lgpl.shtml //// +//// //// +////////////////////////////////////////////////////////////////////// + +module tiny_spi( + // system + input rst_i, + input clk_i, + // memory mapped + input stb_i, + input we_i, + output reg [31:0] dat_o, + input [31:0] dat_i, + output int_o, + input [2:0] adr_i, + // input cyc_i, // comment out for avalon + // output ack_o, // comment out for avalon + + // spi + output MOSI, + output SCLK, + input MISO, + output reg CS + ); + + parameter BAUD_WIDTH = 8; + parameter BAUD_DIV = 0; + parameter SPI_MODE = 0; + parameter BC_WIDTH = 3; + parameter DIV_WIDTH = BAUD_DIV ? $clog2(BAUD_DIV / 2 - 1) : BAUD_WIDTH; + + + reg [7:0] sr8, bb8; + wire [7:0] sr8_sf; + reg [BC_WIDTH - 1:0] bc, bc_next; + reg [DIV_WIDTH - 1:0] ccr; + reg [DIV_WIDTH - 1:0] cc, cc_next; + wire misod; + wire cstb, wstb, bstb, istb; + reg sck; + reg sf, ld; + reg bba; // buffer flag + reg txren, txeen; + wire txr, txe; + wire cpol, cpha; + reg cpolr, cphar; + wire wr; + wire cyc_i; // comment out for wishbone + wire ack_o; // comment out for wishbone + assign cyc_i = 1'b1; // comment out for wishbone + assign ack_o = stb_i & cyc_i; // zero wait + assign wr = stb_i & cyc_i & we_i & ack_o; + assign wstb = wr & (adr_i == 1); + assign istb = wr & (adr_i == 2); + assign cstb = wr & (adr_i == 3); + assign bstb = wr & (adr_i == 4); + assign sr8_sf = { sr8[6:0],misod }; + + localparam + IDLE = 0, + PHASE1 = 1, + PHASE2 = 2 + ; + + reg [1:0] spi_seq, spi_seq_next; + always @(posedge clk_i or posedge rst_i) + if (rst_i) + spi_seq <= IDLE; + else + spi_seq <= spi_seq_next; + + always @(posedge clk_i) + begin + cc <= cc_next; + bc <= bc_next; + dat_o <= + (sr8 & {8{(adr_i == 0)}}) + | (bb8 & {8{(adr_i == 1)}}) + | ({ txr, txe } & {8{(adr_i == 2)}}) + ; + end + + always @(/*AS*/bba or bc or cc or ccr or cpha or cpol or spi_seq) + begin + sck = cpol; + cc_next = BAUD_DIV ? (BAUD_DIV / 2 - 1) : ccr; + bc_next = bc; + ld = 1'b0; + sf = 1'b0; + + case (spi_seq) + IDLE: + begin + if (bba) + begin + bc_next = 7; + ld = 1'b1; + spi_seq_next = PHASE2; + end + else + spi_seq_next = IDLE; + end + PHASE2: + begin + sck = (cpol ^ cpha); + if (cc == 0) + spi_seq_next = PHASE1; + else + begin + cc_next = cc - 1; + spi_seq_next = PHASE2; + end + end + PHASE1: + begin + sck = ~(cpol ^ cpha); + if (cc == 0) + begin + bc_next = bc -1; + sf = 1'b1; + if (bc == 0) + begin + if (bba) + begin + bc_next = 7; + ld = 1'b1; + spi_seq_next = PHASE2; + end + else + spi_seq_next = IDLE; + end + else + spi_seq_next = PHASE2; + end + else + begin + cc_next = cc - 1; + spi_seq_next = PHASE1; + end + end + endcase + end // always @ (... + + always @(posedge clk_i) + begin + if (cstb) // control reg + { CS, cpolr, cphar } <= dat_i; + else + { CS, cpolr, cphar } <= { CS, cpolr, cphar }; + + if (istb) // irq enable reg + { txren, txeen } <= dat_i; + else + { txren, txeen } <= { txren, txeen }; + + if (bstb) // baud reg + ccr <= dat_i; + else + ccr <= ccr; + + if (ld) // shift reg + sr8 <= bb8; + else if (sf) + sr8 <= sr8_sf; + else + sr8 <= sr8; + + if (wstb) // buffer reg + bb8 <= dat_i; + else if (ld) + bb8 <= (spi_seq == IDLE) ? sr8 : sr8_sf; + else + bb8 <= bb8; + end // always @ (posedge clk_i) + + always @(posedge clk_i or posedge rst_i) + begin + if (rst_i) + bba <= 1'b0; + else if (wstb) + bba <= 1'b1; + else if (ld) + bba <= 1'b0; + else + bba <= bba; + end + + assign { cpol, cpha } = ((SPI_MODE >= 0) & (SPI_MODE < 4)) ? + SPI_MODE : { cpolr, cphar }; + assign txe = (spi_seq == IDLE); + assign txr = ~bba; + assign int_o = (txr & txren) | (txe & txeen); + assign SCLK = sck; + assign MOSI = sr8[7]; + assign misod = MISO; + +endmodule diff --git a/rtl/tiny_spi_hw.tcl b/rtl/tiny_spi_hw.tcl new file mode 100644 index 0000000..a2d807f --- /dev/null +++ b/rtl/tiny_spi_hw.tcl @@ -0,0 +1,158 @@ +# +----------------------------------- +# | +# | tiny_spi "tiny_spi" v1.0 +# | Thomas Chou 2010.01.19.18:07:51 +# | SPI 8 bits +# | +# | tiny_spi/hdl/tiny_spi.v +# | +# | ./hdl/tiny_spi.v syn, sim +# | +# +----------------------------------- + +package require -exact qsys 16.1 + +# +----------------------------------- +# | module tiny_spi +# | +set_module_property DESCRIPTION "tiny SPI 8 bits" +set_module_property NAME tiny_spi +set_module_property VERSION 1.0 +set_module_property INTERNAL false +set_module_property OPAQUE_ADDRESS_MAP true +set_module_property GROUP ipTronix +set_module_property AUTHOR "" +set_module_property DISPLAY_NAME "Tiny SPI" +set_module_property INSTANTIATE_IN_SYSTEM_MODULE true +set_module_property EDITABLE true +set_module_property REPORT_TO_TALKBACK false +set_module_property ALLOW_GREYBOX_GENERATION false +set_module_property REPORT_HIERARCHY false +# | +# +----------------------------------- + +# +----------------------------------- +# | files +# | +add_fileset QUARTUS_SYNTH QUARTUS_SYNTH "" "" +set_fileset_property QUARTUS_SYNTH TOP_LEVEL tiny_spi +set_fileset_property QUARTUS_SYNTH ENABLE_RELATIVE_INCLUDE_PATHS false +set_fileset_property QUARTUS_SYNTH ENABLE_FILE_OVERWRITE_MODE false +add_fileset_file tiny_spi.v VERILOG PATH tiny_spi.v TOP_LEVEL_FILE +# | +# +----------------------------------- + +# +----------------------------------- +# | parameters +# | +# | +# +----------------------------------- +add_parameter BAUD_WIDTH INTEGER 8 +set_parameter_property BAUD_WIDTH DEFAULT_VALUE 8 +set_parameter_property BAUD_WIDTH DISPLAY_NAME BAUD_WIDTH +set_parameter_property BAUD_WIDTH UNITS None +set_parameter_property BAUD_WIDTH AFFECTS_GENERATION false +set_parameter_property BAUD_WIDTH HDL_PARAMETER true +add_parameter BAUD_DIV INTEGER 0 +set_parameter_property BAUD_DIV DEFAULT_VALUE 0 +set_parameter_property BAUD_DIV DISPLAY_NAME BAUD_DIV +set_parameter_property BAUD_DIV UNITS None +set_parameter_property BAUD_DIV AFFECTS_GENERATION false +set_parameter_property BAUD_DIV HDL_PARAMETER true +add_parameter SPI_MODE INTEGER 0 +set_parameter_property SPI_MODE DEFAULT_VALUE 0 +set_parameter_property SPI_MODE DISPLAY_NAME SPI_MODE +set_parameter_property SPI_MODE UNITS None +set_parameter_property SPI_MODE AFFECTS_GENERATION false +set_parameter_property SPI_MODE HDL_PARAMETER true + +# +----------------------------------- +# | display items +# | +# | +# +----------------------------------- + +# +----------------------------------- +# | connection point avalon_slave +# | +add_interface avalon_slave avalon end +set_interface_property avalon_slave addressAlignment NATIVE +set_interface_property avalon_slave addressUnits WORDS +set_interface_property avalon_slave associatedClock clk +set_interface_property avalon_slave associatedReset reset +set_interface_property avalon_slave burstOnBurstBoundariesOnly false +set_interface_property avalon_slave explicitAddressSpan 0 +set_interface_property avalon_slave holdTime 0 +set_interface_property avalon_slave isMemoryDevice false +set_interface_property avalon_slave isNonVolatileStorage false +set_interface_property avalon_slave linewrapBursts false +set_interface_property avalon_slave maximumPendingReadTransactions 0 +set_interface_property avalon_slave printableDevice false +set_interface_property avalon_slave readLatency 1 +set_interface_property avalon_slave readWaitStates 0 +set_interface_property avalon_slave readWaitTime 0 +set_interface_property avalon_slave setupTime 0 +set_interface_property avalon_slave timingUnits Cycles +set_interface_property avalon_slave writeWaitTime 0 + +set_interface_property avalon_slave ENABLED true + +add_interface_port avalon_slave stb_i chipselect Input 1 +add_interface_port avalon_slave we_i write Input 1 +add_interface_port avalon_slave dat_i writedata Input 32 +add_interface_port avalon_slave adr_i address Input 3 +add_interface_port avalon_slave dat_o readdata Output 32 +# | +# +----------------------------------- + +# +----------------------------------- +# | connection point clk +# | +add_interface clk clock end + +set_interface_property clk ENABLED true + +add_interface_port clk clk_i clk Input 1 +# | +# +----------------------------------- + + +# +----------------------------------- +# | connection point reset +# | +add_interface reset reset end +set_interface_property reset associatedClock clk +set_interface_property reset synchronousEdges DEASSERT + +set_interface_property reset ENABLED true + +add_interface_port reset rst_i reset Input 1 +# | +# +----------------------------------- + +# +----------------------------------- +# | connection point irq +# | +add_interface irq interrupt end +set_interface_property irq associatedAddressablePoint avalon_slave +set_interface_property irq associatedClock clk +set_interface_property irq associatedReset reset +set_interface_property irq ENABLED true + +add_interface_port irq int_o irq Output 1 +# | +# +----------------------------------- + +# +----------------------------------- +# | connection point spi +# | +add_interface spi conduit end + +set_interface_property spi ENABLED true + +add_interface_port spi MOSI MOSI Output 1 +add_interface_port spi SCLK SCLK Output 1 +add_interface_port spi MISO MISO Input 1 +add_interface_port spi CS CS Output 1 +# | +# +----------------------------------- diff --git a/softcore/inc/tspi.h b/softcore/inc/tspi.h new file mode 100644 index 0000000..c2becbc --- /dev/null +++ b/softcore/inc/tspi.h @@ -0,0 +1,57 @@ +/** + * Copyright 2018 ARDUINO SA (http://www.arduino.cc/) + * This file is part of Vidor IP. + * Copyright (c) 2018 + * Authors: Massimiliano Agneni + * + * This software is released under: + * The GNU General Public License, which covers the main part of Vidor IP + * The terms of this license can be found at: + * https://www.gnu.org/licenses/gpl-3.0.en.html + * + * You can be released from the requirements of the above licenses by purchasing + * a commercial license. Buying such a license is mandatory if you want to modify or + * otherwise use the software for commercial activities involving the Arduino + * software without disclosing the source code of your own applications. To purchase + * a commercial license, send an email to license@arduino.cc. + * + */ + +#ifndef TSPI_H_ +#define TSPI_H_ + +#include + +/** + * IP configuration + */ +#define TSPI_UID 0xEBCE5 +#define TSPI_IP_VER 0x0000 +#define TSPI_DRV_VER 0x0000 +#define TSPI_VER (((TSPI_IP_VER)<<16)|(TSPI_DRV_VER)) + +#define TSPI_PIN_FNC_MOSI PIN_FNC(0x01, PIN_DIR_O) +#define TSPI_PIN_FNC_MISO PIN_FNC(0x02, PIN_DIR_I) +#define TSPI_PIN_FNC_CLK PIN_FNC(0x03, PIN_DIR_O) +#define TSPI_PIN_FNC_SS PIN_FNC(0x04, PIN_DIR_O) + +/** + * private data structure definition + */ +typedef struct tspi_dev_s{ + alt_u8 ss_auto; + alt_u8 mode; + alt_u8 bit_order; +} sTspiDev, *psTspiDev; + +#define TSPI_DEV(name) sTspiDev name##_DEV + +void tspiRpc(void); + +alt_u32 tspiSetup(alt_u32 cmd); +alt_u32 tspiModeSet(alt_u32 cmd, alt_u32 baud, alt_u32 mode, alt_u32 bit_order, alt_u32 ss_auto); +alt_u32 tspiEnd(alt_u32 cmd); +alt_u32 tspiTrx(alt_u32 cmd, alt_u8* buf, alt_u32 len); +alt_u32 tspiTrc(alt_u32 cmd, alt_u32 txl, alt_u8* txb, alt_u32 rxl, alt_u8* rxb); + +#endif /* TSPI_H_ */ diff --git a/softcore/src/tspi.c b/softcore/src/tspi.c new file mode 100644 index 0000000..76de936 --- /dev/null +++ b/softcore/src/tspi.c @@ -0,0 +1,204 @@ +/** + * Copyright 2018 ARDUINO SA (http://www.arduino.cc/) + * This file is part of Vidor IP. + * Copyright (c) 2018 + * Authors: Massimiliano Agneni + * + * This software is released under: + * The GNU General Public License, which covers the main part of Vidor IP + * The terms of this license can be found at: + * https://www.gnu.org/licenses/gpl-3.0.en.html + * + * You can be released from the requirements of the above licenses by purchasing + * a commercial license. Buying such a license is mandatory if you want to modify or + * otherwise use the software for commercial activities involving the Arduino + * software without disclosing the source code of your own applications. To purchase + * a commercial license, send an email to license@arduino.cc. + * + */ + +#define TSPI_RPC_CMD 1 + +#include + + +#include "config.h" + +#if defined(TSPI_RPC_CMD) && (TSPI_RPC_CMD == 1) + #include "mb.h" +#endif /* defined(TSPI_RPC_CMD) && (TSPI_RPC_CMD == 1) */ + +#include "tspi.h" + +#define TSPI_RXDATA 0 +#define TSPI_TXDATA 1 +#define TSPI_STATUS 2 +#define TSPI_CONTROL 3 +#define TSPI_BAUD 4 + +#define TSPI_STATUS_TXE 0x1 +#define TSPI_STATUS_TXR 0x2 + +#define TSPI_SS_H 0x04 +#define TSPI_SS_L 0x00 + +#define TSPI_CPHA 0x01 +#define TSPI_CPOL 0x02 + +#define TSPI_MODE_0 (0|0) +#define TSPI_MODE_1 (0|TSPI_CPHA) +#define TSPI_MODE_2 (TSPI_CPOL|0) +#define TSPI_MODE_3 (TSPI_CPOL|TSPI_CPHA) + +#if defined(TSPI_RPC_CMD) && (TSPI_RPC_CMD == 1) + +/** + * + */ +void tspiRpc(void) +{ + alt_u32 volatile *rpc = mbPtrGet(); + alt_u32 ret; + + ret = -1; + if ((fpgaIp[RPC_GIID(rpc[0])].disc & 0xFFFFF) != TSPI_UID) { + rpc[1] = ret; + return ; + } + switch (RPC_PID(rpc[0])) { + case 2: + ret = tspiSetup(rpc[0]); + break; + case 4: + ret = tspiEnd(rpc[0]); + break; + case 5: + ret = tspiModeSet(rpc[0], rpc[1], rpc[2], rpc[3], rpc[4]); + break; + case 6: + ret = tspiTrx(rpc[0], (alt_u8*)&rpc[2], rpc[1]); + break; + case 7: + tspiTrx(rpc[0], (alt_u8*)&rpc[1], 1); + ret = rpc[1]; + break; + } + rpc[1] = ret; +} +#endif /* defined(TSPI_RPC_CMD) && (TSPI_RPC_CMD == 1) */ + +/** + */ +alt_u32 tspiSetup(alt_u32 cmd) +{ + //alt_u8 giid = RPC_GIID(cmd); + return 0; +} + +/** + */ +alt_u32 tspiEnd(alt_u32 cmd) +{ + return 0; +} + +/** + */ +alt_u32 tspiModeSet(alt_u32 cmd, alt_u32 baud, alt_u32 mode, alt_u32 bit_order, alt_u32 ss_auto) +{ + alt_u8 giid = RPC_GIID(cmd); + psTspiDev pDev = (psTspiDev)fpgaIp[giid].priv; + alt_u32 base = fpgaIp[giid].base; + + switch (mode) { + case 0: pDev->mode = TSPI_MODE_0; break; + case 1: pDev->mode = TSPI_MODE_1; break; + case 2: pDev->mode = TSPI_MODE_2; break; + case 3: pDev->mode = TSPI_MODE_3; break; + default: return -1; + } + pDev->bit_order = bit_order; + pDev->ss_auto = ss_auto; + + IOWR(base, TSPI_CONTROL, TSPI_SS_H | pDev->mode); + IOWR(base, TSPI_BAUD, ALT_CPU_FREQ / (baud*2)); + if (pDev->ss_auto) { + IOWR(base, TSPI_CONTROL, TSPI_SS_H | pDev->mode); + } + + return 0; +} + +/** + */ +alt_u32 tspiTrx(alt_u32 cmd, alt_u8* buf, alt_u32 len) +{ + alt_u8 giid = RPC_GIID(cmd); + psTspiDev pDev = (psTspiDev)fpgaIp[giid].priv; + alt_u32 base = fpgaIp[giid].base; + alt_u8 *rxb = buf; + + /* set SS */ + if (pDev->ss_auto) { + IOWR(base, TSPI_CONTROL, TSPI_SS_L | pDev->mode); + } + + IOWR(base, TSPI_TXDATA, *buf++); + len--; + if (len) { + IOWR(base, TSPI_TXDATA, *buf++); + } + while (len--) { + while (!(IORD(base, TSPI_STATUS) & TSPI_STATUS_TXR)); + IOWR(base, TSPI_TXDATA, *buf++); + *rxb++ = IORD(base, TSPI_RXDATA); + } + while (!(IORD(base, TSPI_STATUS) & TSPI_STATUS_TXE)); + *rxb++ = IORD(base, TSPI_RXDATA); + + /* clear SS */ + if (pDev->ss_auto) { + IOWR(base, TSPI_CONTROL, TSPI_SS_H | pDev->mode); + } + + return 0; +} + +/** + */ +alt_u32 tspiTrc(alt_u32 cmd, alt_u32 txl, alt_u8* txb, alt_u32 rxl, alt_u8* rxb) +{ + alt_u8 giid = RPC_GIID(cmd); + psTspiDev pDev = (psTspiDev)fpgaIp[giid].priv; + alt_u32 base = fpgaIp[giid].base; + int i; + alt_u32 bytes = txl+rxl; + + /* set SS */ + if (pDev->ss_auto) { + IOWR(base, TSPI_CONTROL, TSPI_SS_L | pDev->mode); + } + + for (i=0; i0) { + while (!(IORD(base, TSPI_STATUS) & TSPI_STATUS_TXR)); + if (i>txl) { + *rxb++ = IORD(base, TSPI_TXDATA); + } + } + } + while (!(IORD(base, TSPI_STATUS) & TSPI_STATUS_TXE)); + if (rxl) { + *rxb++ = IORD(base, TSPI_RXDATA); + } + + /* clear SS */ + if (pDev->ss_auto) { + IOWR(base, TSPI_CONTROL, TSPI_SS_H | pDev->mode); + } + return 0; +} diff --git a/softcore/tiny_spi_sw.tcl b/softcore/tiny_spi_sw.tcl new file mode 100644 index 0000000..af8480d --- /dev/null +++ b/softcore/tiny_spi_sw.tcl @@ -0,0 +1,53 @@ +# Copyright 2018 ARDUINO SA (http://www.arduino.cc/) +# This file is part of Vidor IP. +# Copyright (c) 2018 +# Authors: Dario Pennisi +# +# This software is released under: +# The GNU General Public License, which covers the main part of +# Vidor IP +# The terms of this license can be found at: +# https://www.gnu.org/licenses/gpl-3.0.en.html +# +# You can be released from the requirements of the above licenses by purchasing +# a commercial license. Buying such a license is mandatory if you want to modify or +# otherwise use the software for commercial activities involving the Arduino +# software without disclosing the source code of your own applications. To purchase +# a commercial license, send an email to license@arduino.cc. + +# Create a new driver +create_driver TSPI + +# Associate it with some hardware known as "TSPI" +set_sw_property hw_class_name tiny_spi + +# The version of this driver +set_sw_property version 18.0 + +# This driver may be incompatible with versions of hardware less +# than specified below. Updates to hardware and device drivers +# rendering the driver incompatible with older versions of +# hardware are noted with this property assignment. +# +# Multiple-Version compatibility was introduced in version 13.1; +# prior versions are therefore excluded. +set_sw_property min_compatible_hw_version 1.0 + +# Interrupt properties: This driver supports both legacy and enhanced +# interrupt APIs, as well as ISR preemption. +set_sw_property isr_preemption_supported true +set_sw_property supported_interrupt_apis "legacy_interrupt_api enhanced_interrupt_api" + +# Initialize the driver in alt_sys_init() +set_sw_property auto_initialize false + +# Location in generated BSP that above sources will be copied into +set_sw_property bsp_subdirectory drivers + +# C/C++ source files +add_sw_property c_source src/tspi.c + +# Include files +add_sw_property include_source inc/tspi.h + +add_sw_property supported_bsp_type HAL