diff --git a/sw/lib/include/platform.h b/sw/lib/include/platform.h index 8781c146..905b91d9 100644 --- a/sw/lib/include/platform.h +++ b/sw/lib/include/platform.h @@ -1,6 +1,5 @@ #ifndef __PLATFORM_H__ #define __PLATFORM_H__ -#include "stddef.h" // ============== Target Independent Definitions ============== @@ -14,6 +13,8 @@ // ============== Target Specific Definitions ============== #ifdef TARGET_HYDROGENSOC + // #define EXCEPTION + #define MEM_ROM_BEGIN 0x00010000 #define MEM_ROM_SIZE 32*1024 // 32 KB @@ -29,6 +30,10 @@ #define GPIO_SETWIDTH 16 #define GPIO_ADDR 0x40002000 + // ---------- SPI ------------ + #define SPI + #define SPI_ADDR 0x40003000 + // ------ CLOCK FREQUENCY -------- #define CLK_FREQ 12000000 diff --git a/sw/lib/include/spi.h b/sw/lib/include/spi.h new file mode 100644 index 00000000..cd5e161c --- /dev/null +++ b/sw/lib/include/spi.h @@ -0,0 +1,87 @@ +#ifndef __SPI_H__ +#define __SPI_H__ +#include +#include + +/** + * Enable: enable/disable SPI + * pol: Polarity + * - true: inactive state of SCK is high + * - false: inactive state of SCK is low + * pha: Phase + * - true: data shifted on leading edge, sampled on trailing edge + * - false: data sampled on leading edge, shifted on trailing edge + * end: Endianness + * - true: LSB first + * - false: MSB first + * Baudrate: number of bits transferred per second + * Post_cs_low_delay: number of cycles to wait after de-asserting CS + * Pre_cs_high_delay: number of cycles to wait before asserting CS +*/ + +typedef enum {CSMODE_AUTO=0b00, CSMODE_DISABLE=0b11} csmode_t; + +struct SPI_Config { + uint32_t base_addr; + bool enable; + bool pol; + bool pha; + bool lsb_first; + uint32_t baudrate; + uint8_t device_num; + csmode_t cs_mode; + uint8_t post_cs_low_delay; + uint8_t pre_cs_high_delay; + bool loopback_enable; +}; + + +#define SPI_Config_defaults { \ + .base_addr = SPI_ADDR, \ + .enable = true, \ + .pha = false, \ + .pol = false, \ + .lsb_first = false, \ + .baudrate = 1000000, \ + .device_num = 0, \ + .cs_mode = CSMODE_AUTO, \ + .post_cs_low_delay = 1, \ + .pre_cs_high_delay = 1, \ + .loopback_enable=false \ +}; \ + +/** + * @brief Initialize SPI + */ +int spi_init(struct SPI_Config * cfg); + +/** + * @brief Starts SPI transaction by deasserting CS pin + * @param cfg config struct + */ +void spi_select(struct SPI_Config * cfg); + +/** + * @brief Finishes SPI transaction by re-asserting CS pin + * @param cfg config struct + */ +void spi_deselect(struct SPI_Config * cfg); + +/** + * @brief Transfer a byte of data (Send as well as recieve) + * @param cfg config struct + * @param b byte to be transferred + */ +char spi_transfer(struct SPI_Config * cfg, char b); + +/** + * @brief Transfer multiple bytes of data (Send as well as recieve) + * @param cfg config struct + * @param send_buf char buffer to send + * @param recv_buf char buffer to recieve data in + * @param len lenth of send buffer + * @return char + */ +char *spi_transfer_buf(struct SPI_Config * cfg, char *send_buf, char *recv_buf, unsigned int len); + +#endif // __SPI_H__ \ No newline at end of file diff --git a/sw/lib/libcatom/hydrogensoc/spi.c b/sw/lib/libcatom/hydrogensoc/spi.c new file mode 100644 index 00000000..36ae7ba4 --- /dev/null +++ b/sw/lib/libcatom/hydrogensoc/spi.c @@ -0,0 +1,117 @@ +#include "platform.h" +#include "mmio.h" +#include "spi.h" + +#define SPI_REG_SCKDIV_OFFSET 0x00 +#define SPI_REG_SCTRL_OFFSET 0x04 +#define SPI_REG_TDATA_OFFSET 0x08 +#define SPI_REG_RDATA_OFFSET 0x0c +#define SPI_REG_CSCTRL_OFFSET 0x10 +#define SPI_REG_DCTRL_OFFSET 0x14 + +#define _spi_busy() (REG32(SPI_ADDR, SPI_REG_SCTRL_OFFSET) >> 31) + +int spi_init(struct SPI_Config * cfg) { + while(_spi_busy()) { + asm volatile(""); + } + + // set divisor reg + uint32_t fratio = (CLK_FREQ/cfg->baudrate); + REG32(cfg->base_addr, SPI_REG_SCKDIV_OFFSET) = fratio - 1; + + // set sctrl reg + uint32_t spi_sctrl = 0; + spi_sctrl = cfg->enable ? bitset(spi_sctrl, 0): spi_sctrl; + spi_sctrl = cfg->pol ? bitset(spi_sctrl, 1): spi_sctrl; + spi_sctrl = cfg->pha ? bitset(spi_sctrl, 2): spi_sctrl; + spi_sctrl = cfg->lsb_first ? bitset(spi_sctrl, 3): spi_sctrl; + spi_sctrl = cfg->loopback_enable ? bitset(spi_sctrl, 4): spi_sctrl; + REG32(cfg->base_addr, SPI_REG_SCTRL_OFFSET) = spi_sctrl; + + uint32_t spi_csctrl = cfg->cs_mode; + if(cfg->cs_mode == CSMODE_AUTO) { + spi_csctrl = bitset(spi_csctrl, 24 + cfg->device_num); + } + else if (cfg->cs_mode == CSMODE_DISABLE) { + // In this case device_num is used by select/deselect functions + spi_csctrl &= 0x00ffffff; // clear active cs + } else { + return 1; // invalid mode + } + REG32(cfg->base_addr, SPI_REG_CSCTRL_OFFSET) = spi_csctrl; + + // set dctrl reg + uint32_t spi_dctrl=0; + spi_dctrl |= ((uint32_t)cfg->pre_cs_high_delay) << 8; + spi_dctrl |= ((uint32_t)cfg->post_cs_low_delay); + REG32(cfg->base_addr, SPI_REG_DCTRL_OFFSET) = spi_dctrl; + + return 0; +} + +char spi_transfer(struct SPI_Config * cfg, char b) { + REG8(cfg->base_addr, SPI_REG_TDATA_OFFSET) = b; + while(_spi_busy()) { + asm volatile(""); + } + return REG8(cfg->base_addr, SPI_REG_RDATA_OFFSET); +} + +void spi_select(struct SPI_Config * cfg) { + REG32(cfg->base_addr, SPI_REG_CSCTRL_OFFSET) = bitset(REG32(cfg->base_addr, SPI_REG_CSCTRL_OFFSET), 24+cfg->device_num); +} + +void spi_deselect(struct SPI_Config * cfg) { + REG32(cfg->base_addr, SPI_REG_CSCTRL_OFFSET) = bitclear(REG32(cfg->base_addr, SPI_REG_CSCTRL_OFFSET), 24+cfg->device_num); +} + +char *spi_transfer_buf(struct SPI_Config * cfg, char *send_buf, char *recv_buf, unsigned int len) +{ + // Disable hardware control + uint32_t curr_csctrl = REG32(cfg->base_addr, SPI_REG_CSCTRL_OFFSET); + REG32(cfg->base_addr, SPI_REG_CSCTRL_OFFSET) = curr_csctrl | 0b11; + + // Select + REG32(cfg->base_addr, SPI_REG_CSCTRL_OFFSET) = bitset(REG32(cfg->base_addr, SPI_REG_CSCTRL_OFFSET), 24+cfg->device_num); + + // transfer + while(len--) { + REG8(cfg->base_addr, SPI_REG_TDATA_OFFSET) = *(send_buf++); + while(_spi_busy()) + asm volatile(""); + *(recv_buf++) = REG8(cfg->base_addr, SPI_REG_RDATA_OFFSET); + } + + // Deselect + REG32(cfg->base_addr, SPI_REG_CSCTRL_OFFSET) = bitclear(REG32(cfg->base_addr, SPI_REG_CSCTRL_OFFSET), 24+cfg->device_num); + + // restore previous mode + REG32(cfg->base_addr, SPI_REG_CSCTRL_OFFSET) = curr_csctrl; + return recv_buf; +} + +char *spi_get_buf(struct SPI_Config * cfg, char send_val, char *recv_buf, unsigned int len) +{ + // Disable hardware control + uint32_t curr_csctrl = REG32(cfg->base_addr, SPI_REG_CSCTRL_OFFSET); + REG32(cfg->base_addr, SPI_REG_CSCTRL_OFFSET) = curr_csctrl | 0b11; + + // Select + REG32(cfg->base_addr, SPI_REG_CSCTRL_OFFSET) = bitset(REG32(cfg->base_addr, SPI_REG_CSCTRL_OFFSET), 24+cfg->device_num); + + // transfer + while(len--) { + REG8(cfg->base_addr, SPI_REG_TDATA_OFFSET) = send_val; + while(_spi_busy()) + asm volatile(""); + *(recv_buf++) = REG8(cfg->base_addr, SPI_REG_RDATA_OFFSET); + } + + // Deselect + REG32(cfg->base_addr, SPI_REG_CSCTRL_OFFSET) = bitclear(REG32(cfg->base_addr, SPI_REG_CSCTRL_OFFSET), 24+cfg->device_num); + + // restore previous mode + REG32(cfg->base_addr, SPI_REG_CSCTRL_OFFSET) = curr_csctrl; + return recv_buf; +} \ No newline at end of file