diff --git a/fpga/zsbl/sd.c b/fpga/zsbl/sd.c index 8781dd5c3..aacea0db9 100644 --- a/fpga/zsbl/sd.c +++ b/fpga/zsbl/sd.c @@ -12,6 +12,7 @@ uint8_t crc7(uint8_t prev, uint8_t in) { return remainder & 0xff; } +// Need to check this. This could be wrong as well. uint16_t crc16(uint16_t crc, uint8_t data) { // CRC polynomial 0x11021 crc = (uint8_t)(crc >> 8) | (crc << 8); @@ -22,13 +23,74 @@ uint16_t crc16(uint16_t crc, uint8_t data) { return crc; } -uint8_t sd_cmd(uint8_t cmd, uint32_t arg, uint8_t crc) { - spi_send_byte +uint64_t sd_cmd(uint8_t cmd, uint32_t arg, uint8_t crc) { + uint8_t response_len; + uint8_t i; + uint64_t r; + uint8_t rbyte; + + switch (cmd) { + case 0: + response_len = 1; + break; + case 8: + response_len = 7 + break; + default: + response_len = 1; + break; + } + + // Make interrupt pending after response fifo receives the correct + // response length. + write_reg(SPI_RXMARK, response_len); + + // Write all 6 bytes into transfer fifo + spi_sendbyte(0x40 | cmd); + spi_sendbyte(arg >> 24); + spi_sendbyte(arg >> 16); + spi_sendbyte(arg >> 8); + spi_sendbyte(arg); + spi_sendbyte(crc); + + // Wait for command to send + // The Transfer IP bit should go high when the txFIFO is empty + // while(!(read_reg(SPI_IP) & 1)) {} + waittx(); + + // Read the dummy rxFIFO entries to move the head back to the tail + for (i = 0; i < 6; i++) { + spi_readbyte(); + } + + // Send "dummy signals". Since SPI is duplex, + // useless bytes must be transferred + for (i = 0; i < response_len; i++) { + spi_sendbyte(0xFF); + } + + // Wait for transfer fifo again + waittx(); + + // Read rxfifo response + for (i = 0; i < response_len; i++) { + rbyte = spi_readbyte(); + r = r | (rbyte << ((response_len - 1 - i)*8)); + } + + return r; } +#define cmd0() sd_cmd( 0, 0x00000000, 0x95) +#define cmd8() sd_cmd( 8, 0x000001aa, 0x87) +// CMD55 has to be sent before ACMD41 (it means the next command is +// application specific) +#define cmd55() sd_cmd(55, 0x00000000, 0x65) +#defube acmd41() sd_cmd(41, 0x40000000, 0x77) + void init_sd(){ init_spi(); - + cmd0() } diff --git a/fpga/zsbl/sd.h b/fpga/zsbl/sd.h index b1b27db14..f08941364 100644 --- a/fpga/zsbl/sd.h +++ b/fpga/zsbl/sd.h @@ -4,6 +4,5 @@ uint8_t crc7(uint8_t prev, uint8_t in); uint16_t crc16(uint16_t crc, uint8_t data); -uint8_t sd_cmd(uint8_t cmd, uint32_t arg, uint8_t crc); +uint64_t sd_cmd(uint8_t cmd, uint32_t arg, uint8_t crc); void init_sd(); - diff --git a/fpga/zsbl/spi.c b/fpga/zsbl/spi.c index bf6d629b4..c812327f4 100644 --- a/fpga/zsbl/spi.c +++ b/fpga/zsbl/spi.c @@ -1,50 +1,87 @@ +/////////////////////////////////////////////////////////////////////// +// spi.c +// +// Written: Jaocb Pease jacob.pease@okstate.edu 7/22/2024 +// +// Purpose: SPI Controller API for bootloader +// +// +// +// A component of the Wally configurable RISC-V project. +// +// Copyright (C) 2021-23 Harvey Mudd College & Oklahoma State University +// +// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 +// +// Licensed under the Solderpad Hardware License v 2.1 (the +// “License”); you may not use this file except in compliance with the +// License, or, at your option, the Apache License version 2.0. You +// may obtain a copy of the License at +// +// https://solderpad.org/licenses/SHL-2.1/ +// +// Unless required by applicable law or agreed to in writing, any work +// distributed under the License is distributed on an “AS IS” BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +// implied. See the License for the specific language governing +// permissions and limitations under the License. +/////////////////////////////////////////////////////////////////////// + #include "spi.h" -void write_reg(uintptr_t addr, uint32_t value) { +// Write to a register +inline void write_reg(uintptr_t addr, uint32_t value) { volatile uint32_t * loc = (volatile uint32_t *) addr; *loc = value; } -void read_red(uintptr_t addr) { +// Read a register +inline void read_reg(uintptr_t addr) { return *(volatile uint32_t *) addr; } +// Queues a single byte in the transfer fifo +inline void spi_sendbyte(uint8_t byte) { + // Write byte to transfer fifo + write_reg(SPI_TXDATA, byte); +} + +inline uint8_t spi_readbyte() { + return read_reg(SPI_RXDATA); +} + +inline void waittx() { + while(!(read_reg(SPI_IP) & 1)) {} +} + +inline void waitrx() { + while(read_reg(SPI_IP) & 2)) {} +} + + // Initialize Sifive FU540 based SPI Controller void spi_init() { - // Disable interrupts by default - // write_reg(SPI_IE, 0); + // Enable interrupts + write_reg(SPI_IE, 0x3); + // Set TXMARK to 1. If the number of entries is < 1 + // IP's txwm field will go high. + // Set RXMARK to 0. If the number of entries is > 0 + // IP's rwxm field will go high. write_reg(SPI_TXMARK, 1); write_reg(SPI_RXMARK, 0); + // Set Delay 0 to default write_reg(SPI_DELAY0, SIFIVE_SPI_DELAY0_CSSCK(1) | SIFIVE_SPI_DELAY0_SCKCS(1)); + // Set Delay 1 to default write_reg(SPI_DELAY1, SIFIVE_SPI_DELAY1_INTERCS(1) | SIFIVE_SPI_DELAY1_INTERXFR(0)); + + // Initialize the SPI controller clock to + // div = (20MHz/(2*400kHz)) - 1 = 24 = 0x18 + write_reg(SPI_SCKDIV, 0x18); } - -// Sends and receives a single byte -uint8_t spi_send_byte(uint8_t byte) { - // Write byte to transfer fifo - write_reg(SPI_TXDATA, byte); - - /* Not sure how necessary this is. Will keep commented for now. - // Wait a decent amount of time for data to send - for (int i = 0; i < 100; i++) { - __asm__ volatile("nop"); - } - */ - - // Wait for data to come into receive fifo - while (read_reg(SPI_IP) != 2) {} - - // Read received data - result = read_reg(SPI_RXDATA); - - // Return result - return result; -} - diff --git a/fpga/zsbl/spi.h b/fpga/zsbl/spi.h index 6bae450ae..a035f0ab5 100644 --- a/fpga/zsbl/spi.h +++ b/fpga/zsbl/spi.h @@ -4,26 +4,28 @@ #include +#define SPI_BASE 0x13000 /* Base address of SPI device used for SDC */ + /* register offsets */ -#define SPI_SCKDIV 0x00 /* Serial clock divisor */ -#define SPI_SCKMODE 0x04 /* Serial clock mode */ -#define SPI_CSID 0x10 /* Chip select ID */ -#define SPI_CSDEF 0x14 /* Chip select default */ -#define SPI_CSMODE 0x18 /* Chip select mode */ -#define SPI_DELAY0 0x28 /* Delay control 0 */ -#define SPI_DELAY1 0x2c /* Delay control 1 */ -#define SPI_FMT 0x40 /* Frame format */ -#define SPI_TXDATA 0x48 /* Tx FIFO data */ -#define SPI_RXDATA 0x4c /* Rx FIFO data */ -#define SPI_TXMARK 0x50 /* Tx FIFO [<35;39;29Mwatermark */ -#define SPI_RXMARK 0x54 /* Rx FIFO watermark */ +#define SPI_SCKDIV SPI_BASE + 0x00 /* Serial clock divisor */ +#define SPI_SCKMODE SPI_BASE + 0x04 /* Serial clock mode */ +#define SPI_CSID SPI_BASE + 0x10 /* Chip select ID */ +#define SPI_CSDEF SPI_BASE + 0x14 /* Chip select default */ +#define SPI_CSMODE SPI_BASE + 0x18 /* Chip select mode */ +#define SPI_DELAY0 SPI_BASE + 0x28 /* Delay control 0 */ +#define SPI_DELAY1 SPI_BASE + 0x2c /* Delay control 1 */ +#define SPI_FMT SPI_BASE + 0x40 /* Frame format */ +#define SPI_TXDATA SPI_BASE + 0x48 /* Tx FIFO data */ +#define SPI_RXDATA SPI_BASE + 0x4c /* Rx FIFO data */ +#define SPI_TXMARK SPI_BASE + 0x50 /* Tx FIFO [<35;39;29Mwatermark */ +#define SPI_RXMARK SPI_BASE + 0x54 /* Rx FIFO watermark */ /* Non-implemented -#define SPI_FCTRL 0x60 // SPI flash interface control -#define SPI_FFMT 0x64 // SPI flash instruction format +#define SPI_FCTRL SPI_BASE + 0x60 // SPI flash interface control +#define SPI_FFMT SPI_BASE + 0x64 // SPI flash instruction format */ -#define SPI_IE 0x70 /* Interrupt Enable Register */ -#define SPI_IP 0x74 /* Interrupt Pendings Register */ +#define SPI_IE SPI_BASE + 0x70 /* Interrupt Enable Register */ +#define SPI_IP SPI_BASE + 0x74 /* Interrupt Pendings Register */ /* delay0 bits */ #define SIFIVE_SPI_DELAY0_CSSCK(x) ((u32)(x)) @@ -37,9 +39,22 @@ #define SIFIVE_SPI_DELAY1_INTERXFR(x) ((u32)(x) << 16) #define SIFIVE_SPI_DELAY1_INTERXFR_MASK (0xffU << 16) -void write_reg(uintptr_t addr, uint32_t value); -uint32_t read_reg(uintptr_t addr); -uint8_t spi_send_byte(uint8_t byte); +/* csmode bits */ +#define SIFIVE_SPI_CSMODE_MODE_AUTO 0U +#define SIFIVE_SPI_CSMODE_MODE_HOLD 2U +#define SIFIVE_SPI_CSMODE_MODE_OFF 3U + + +#define WAITTX while(!(read_reg(SPI_IP) & 1) {} +#define WAITRX while(read_reg(SPI_IP) & 2) {} + +inline void write_reg(uintptr_t addr, uint32_t value); +inline uint32_t read_reg(uintptr_t addr); +inline void spi_sendbyte(uint8_t byte); +inline void waittx(); +inline void waitrx(); +uint8_t spi_txrx(uint8_t byte); +inline uint8_t spi_readbyte(); void spi_init();