-
Notifications
You must be signed in to change notification settings - Fork 83
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add example_spi_host_quadIO
#401
Merged
Merged
Changes from 7 commits
Commits
Show all changes
25 commits
Select commit
Hold shift + click to select a range
8696bb9
QuadSPI example, not working in Questasim
2222baf
Merge branch 'esl-epfl:main' into main
consanii c1c4ba3
Add quadIO SPI example
f40bfce
Merge branch 'esl-epfl:main' into main
consanii 0dad5da
Remove old quadspi example
7e937c9
Merge branch 'main' of github.com:consanii/x-heep
6f5235a
Code cleanup, fix to also support fisical flash
800e97c
QE bit set, small changes
de79010
small fix
b2e1a5b
small fix
9311a54
Merge branch 'esl-epfl:main' into main
consanii 2d021b0
Write Enable, status reg2 check
consanii b45959b
Merge branch 'esl-epfl:main' into main
consanii 631e324
Merge branch 'esl-epfl:main' into main
consanii 643dc26
Tested on FPGA
consanii e585f5f
small fix
consanii 8ee13f9
Merge branch 'esl-epfl:main' into main
consanii 2fdd12b
Disable QE bit before programming
consanii b9d714b
Merge branch 'main' of github.com:consanii/x-heep into main
consanii 0af5c43
Debug QE bit set
consanii 08e3516
Fix QE bit set bug
consanii d47946a
boh
consanii e15865c
Revert "boh"
consanii a3e7cfd
boh2
consanii 8fb317c
boh3
consanii File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,309 @@ | ||
// Copyright EPFL contributors. | ||
// Licensed under the Apache License, Version 2.0, see LICENSE for details. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
/** | ||
* \brief Fast Read Quad I/O SPI Host example | ||
* | ||
* Simple example to check the Fast Read Quad I/O SPI_host functionality. | ||
* It checks that the ram and flash have the same content. | ||
* | ||
* \author Mattia Consani, EPFL | ||
*/ | ||
|
||
|
||
#include <stdio.h> | ||
#include <stdint.h> | ||
#include <stdlib.h> | ||
|
||
#include "core_v_mini_mcu.h" | ||
#include "csr.h" | ||
#include "hart.h" | ||
#include "handler.h" | ||
#include "soc_ctrl.h" | ||
#include "spi_host.h" | ||
#include "fast_intr_ctrl.h" | ||
#include "fast_intr_ctrl_regs.h" | ||
#include "x-heep.h" | ||
|
||
|
||
// W25Q128JW flash commands supported by Questasim flash model | ||
// Also FFh and EDh are supported by the simulation model, but not by the phisical flash | ||
#define W25Q128JW_CMD_RELEASE_POWERDOWN 0xab | ||
#define W25Q128JW_CMD_POWERDOWN 0xb9 | ||
#define W25Q128JW_CMD_READ 0x03 | ||
#define W25Q128JW_CMD_READ_DUALIO 0xbb | ||
#define W25Q128JW_CMD_READ_QUADIO 0xeb | ||
|
||
|
||
#ifdef TARGET_PYNQ_Z2 | ||
#define USE_SPI_FLASH | ||
#endif | ||
|
||
|
||
#define PRINTF_IN_FPGA 1 | ||
#define PRINTF_IN_SIM 0 | ||
|
||
#if TARGET_SIM && PRINTF_IN_SIM | ||
#define PRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__) | ||
#elif TARGET_PYNQ_Z2 && PRINTF_IN_FPGA | ||
#define PRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__) | ||
#else | ||
#define PRINTF(...) | ||
#endif | ||
|
||
|
||
#define REVERT_24b_ADDR(addr) ((((uint32_t)(addr) & 0xff0000) >> 16) | ((uint32_t)(addr) & 0xff00) | (((uint32_t)(addr) & 0xff) << 16)) | ||
|
||
#define FLASH_CLK_MAX_HZ (133*1000*1000) // In Hz (133 MHz for the flash w25q128jvsim used in the EPFL Programmer) | ||
|
||
volatile int8_t spi_intr_flag; | ||
spi_host_t spi_host; | ||
uint32_t flash_data[8]; | ||
uint32_t flash_original[8] = {1}; | ||
|
||
#ifndef USE_SPI_FLASH | ||
void fic_irq_spi(void) | ||
{ | ||
// Disable SPI interrupts | ||
spi_enable_evt_intr(&spi_host, false); | ||
spi_enable_rxwm_intr(&spi_host, false); | ||
spi_intr_flag = 1; | ||
} | ||
#else | ||
void fic_irq_spi_flash(void) | ||
{ | ||
// Disable SPI interrupts | ||
spi_enable_evt_intr(&spi_host, false); | ||
spi_enable_rxwm_intr(&spi_host, false); | ||
spi_intr_flag = 1; | ||
} | ||
#endif | ||
|
||
|
||
int main(int argc, char *argv[]) | ||
{ | ||
PRINTF("Quad I/O SPI Host example\n\r"); | ||
|
||
soc_ctrl_t soc_ctrl; | ||
soc_ctrl.base_addr = mmio_region_from_addr((uintptr_t)SOC_CTRL_START_ADDRESS); | ||
|
||
// [WARNING]: this part was not updated to support quad SPI | ||
if ( get_spi_flash_mode(&soc_ctrl) == SOC_CTRL_SPI_FLASH_MODE_SPIMEMIO ) | ||
{ | ||
#ifdef USE_SPI_FLASH | ||
PRINTF("This application cannot work with the memory mapped SPI FLASH module - do not use the FLASH_EXEC linker script for this application\n"); | ||
return EXIT_SUCCESS; | ||
#else | ||
PRINTF("This application is not supporting quad SPI in memory mapped mode (yet)\n"); | ||
return EXIT_FAILURE; | ||
/* | ||
if we are using in SIMULATION the SPIMMIO from Yosys, then the flash_original data is different | ||
as the compilation is done differently, so we will store there the first WORDs of code mapped at the beginning of the FLASH | ||
*/ | ||
uint32_t* ptr_flash = (uint32_t*)FLASH_MEM_START_ADDRESS; | ||
for(int i =0; i < 8 ; i++){ | ||
flash_original[i] = ptr_flash[i]; | ||
} | ||
// we read the data from the FLASH address 0x0, which corresponds to FLASH_MEM_START_ADDRESS | ||
uint32_t read_byte_cmd_spimemio = ((REVERT_24b_ADDR(0x0) << 8) | 0x03); // The address bytes sent through the SPI to the Flash are in reverse order | ||
#endif | ||
} | ||
|
||
|
||
// spi_host_t spi_host; | ||
#ifndef USE_SPI_FLASH | ||
spi_host.base_addr = mmio_region_from_addr((uintptr_t)SPI_HOST_START_ADDRESS); | ||
#else | ||
spi_host.base_addr = mmio_region_from_addr((uintptr_t)SPI_FLASH_START_ADDRESS); | ||
#endif | ||
|
||
uint32_t core_clk = soc_ctrl_get_frequency(&soc_ctrl); | ||
|
||
// Enable interrupt on processor side | ||
// Enable global interrupt for machine-level interrupts | ||
CSR_SET_BITS(CSR_REG_MSTATUS, 0x8); | ||
// Set mie.MEIE bit to one to enable machine-level fast spi interrupt | ||
#ifndef USE_SPI_FLASH | ||
const uint32_t mask = 1 << 20; | ||
#else | ||
const uint32_t mask = 1 << 21; | ||
#endif | ||
CSR_SET_BITS(CSR_REG_MIE, mask); | ||
spi_intr_flag = 0; | ||
|
||
#ifdef USE_SPI_FLASH | ||
// Select SPI host as SPI output | ||
soc_ctrl_select_spi_host(&soc_ctrl); | ||
#endif | ||
|
||
|
||
// Enable SPI host device | ||
spi_set_enable(&spi_host, true); | ||
|
||
// Enable event interrupt | ||
spi_enable_evt_intr(&spi_host, true); | ||
// Enable RX watermark interrupt | ||
spi_enable_rxwm_intr(&spi_host, true); | ||
// Enable SPI output | ||
spi_output_enable(&spi_host, true); | ||
|
||
// Configure SPI clock | ||
// SPI clk freq = 1/2 core clk freq when clk_div = 0 | ||
// SPI_CLK = CORE_CLK/(2 + 2 * CLK_DIV) <= CLK_MAX => CLK_DIV > (CORE_CLK/CLK_MAX - 2)/2 | ||
uint16_t clk_div = 0; | ||
if(FLASH_CLK_MAX_HZ < core_clk/2){ | ||
clk_div = (core_clk/(FLASH_CLK_MAX_HZ) - 2)/2; // The value is truncated | ||
if (core_clk/(2 + 2 * clk_div) > FLASH_CLK_MAX_HZ) clk_div += 1; // Adjust if the truncation was not 0 | ||
} | ||
// SPI Configuration | ||
// Configure chip 0 (flash memory) | ||
const uint32_t chip_cfg = spi_create_configopts((spi_configopts_t){ | ||
.clkdiv = clk_div, | ||
.csnidle = 0xF, | ||
.csntrail = 0xF, | ||
.csnlead = 0xF, | ||
.fullcyc = false, | ||
.cpha = 0, | ||
.cpol = 0 | ||
}); | ||
spi_set_configopts(&spi_host, 0, chip_cfg); | ||
spi_set_csid(&spi_host, 0); | ||
|
||
// Set RX watermark to 8 word | ||
spi_set_rx_watermark(&spi_host, 8); | ||
|
||
uint32_t *flash_data_ptr = flash_data[0]; | ||
|
||
|
||
// ----------------COMMAND---------------- | ||
// Power up flash | ||
// ----------------COMMAND---------------- | ||
|
||
// Create segment 1 | ||
const uint32_t powerup_byte_cmd = W25Q128JW_CMD_RELEASE_POWERDOWN; | ||
spi_write_word(&spi_host, powerup_byte_cmd); | ||
|
||
const uint32_t cmd_powerup = spi_create_command((spi_command_t){ | ||
.len = 0, // 1 Byte | ||
.csaat = false, // End command | ||
.speed = kSpiSpeedStandard, // Single speed | ||
.direction = kSpiDirTxOnly // Write only | ||
}); | ||
spi_set_command(&spi_host, cmd_powerup); | ||
spi_wait_for_ready(&spi_host); | ||
// ----------------END COMMAND---------------- | ||
|
||
|
||
volatile uint32_t data_addr = flash_original; | ||
|
||
|
||
// ----------------COMMAND---------------- | ||
// Fast Read Quad I/O | ||
// ----------------COMMAND---------------- | ||
|
||
// Create segment 1 | ||
uint32_t cmd_read_quadIO = W25Q128JW_CMD_READ_QUADIO; | ||
spi_write_word(&spi_host, cmd_read_quadIO); | ||
spi_wait_for_ready(&spi_host); | ||
|
||
const uint32_t cmd_read = spi_create_command((spi_command_t){ | ||
.len = 0, // 1 Byte | ||
.csaat = true, // Command not finished | ||
.speed = kSpiSpeedStandard, // Single speed | ||
.direction = kSpiDirTxOnly // Write only | ||
}); | ||
spi_set_command(&spi_host, cmd_read); | ||
spi_wait_for_ready(&spi_host); | ||
|
||
|
||
// Create segment 2 | ||
uint32_t read_byte_cmd = (REVERT_24b_ADDR(flash_original) | 0xFF << 24); // Fxh (here FFh) required by W25Q128JW | ||
PRINTF("read_byte_cmd = %x\n\r", read_byte_cmd); | ||
spi_write_word(&spi_host, read_byte_cmd); | ||
spi_wait_for_ready(&spi_host); | ||
|
||
const uint32_t cmd_address = spi_create_command((spi_command_t){ | ||
.len = 3, // 3 Byte | ||
.csaat = true, // Command not finished | ||
.speed = kSpiSpeedQuad, // Quad speed | ||
.direction = kSpiDirTxOnly // Write only | ||
}); | ||
spi_set_command(&spi_host, cmd_address); | ||
spi_wait_for_ready(&spi_host); | ||
|
||
|
||
// Create segment 3 | ||
const uint32_t dummy_clocks_cmd = spi_create_command((spi_command_t){ | ||
#ifdef TARGET_PYNQ_Z2 | ||
.len = 3, // W25Q128JW flash needs 4 dummy cycles | ||
#else | ||
.len = 7, // SPI flash simulation model needs 8 dummy cycles | ||
#endif | ||
.csaat = true, // Command not finished | ||
.speed = kSpiSpeedQuad, // Quad speed | ||
.direction = kSpiDirDummy // Dummy | ||
}); | ||
spi_set_command(&spi_host, dummy_clocks_cmd); | ||
spi_wait_for_ready(&spi_host); | ||
|
||
|
||
// Create segment 4 | ||
const uint32_t cmd_read_rx = spi_create_command((spi_command_t){ | ||
.len = 31, // 32 Byte | ||
.csaat = false, // End command | ||
.speed = kSpiSpeedQuad, // Quad speed | ||
.direction = kSpiDirRxOnly // Read only | ||
}); | ||
spi_set_command(&spi_host, cmd_read_rx); | ||
spi_wait_for_ready(&spi_host); | ||
// ----------------END COMMAND---------------- | ||
|
||
|
||
|
||
// Wait transaction is finished (polling register) | ||
// spi_wait_for_rx_watermark(&spi_host); | ||
// or wait for SPI interrupt | ||
PRINTF("Waiting for SPI...\n\r"); | ||
|
||
while( spi_intr_flag == 0 ) { | ||
CSR_CLEAR_BITS(CSR_REG_MSTATUS, 0x8); | ||
|
||
if( spi_intr_flag == 0 ) | ||
wait_for_interrupt(); | ||
|
||
CSR_SET_BITS(CSR_REG_MSTATUS, 0x8); | ||
} | ||
|
||
// Enable event interrupt | ||
spi_enable_evt_intr(&spi_host, true); | ||
// Enable RX watermark interrupt | ||
spi_enable_rxwm_intr(&spi_host, true); | ||
|
||
// Read data from SPI RX FIFO | ||
for (int i=0; i<8; i++) { | ||
spi_read_word(&spi_host, &flash_data[i]); | ||
} | ||
|
||
PRINTF("flash vs ram...\n\r"); | ||
|
||
uint32_t errors = 0; | ||
uint32_t* ram_ptr = flash_original; | ||
for (int i=0; i<8; i++) { | ||
if(flash_data[i] != *ram_ptr) { | ||
PRINTF("@%x : %x != %x\n\r", ram_ptr, flash_data[i], *ram_ptr); | ||
errors++; | ||
} | ||
ram_ptr++; | ||
} | ||
|
||
if (errors == 0) { | ||
PRINTF("success!\n\r"); | ||
} else { | ||
PRINTF("failure, %d errors!\n\r", errors); | ||
return EXIT_FAILURE; | ||
} | ||
|
||
return EXIT_SUCCESS; | ||
|
||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
W25Q128JW datasheet on page 33 describe the behaviour of line 221.
In short, after the command code at single speed the flash expect to receive the memory address MSB first at quad speed(so a Byte swap is require in a little-endian architecture). After it, a dummy byte at quad speed of value Fxh is required.
A detailed showcase of this behaviour can be also found in one OpenTitan diagram.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sorry I was not clear, what I meant this comment
Fxh (here FFh) required by W25Q128JW
I do not understand what Fxh meansThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I found no clear description on the data sheet. I though it is an hexadecimal with first 4 bits all ones (F) and the next four can be whatever (x).
Anyway in the opentitan documentation (linked in the message before) I saw a diagram with the byte after the address with all ones so I decided to implement it like opentitan (FFh).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok thanks for the clarification
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
another question @consanii , I see in the single mode SPI examples that the address is shifted by 8 after reversed, and then ored with
03
, here you do reverse, or withff
, but I do not see the shift by 8, why?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because the behaviour in single and quad modes differs. In quad mode, the command code must be sent at standard speed before sending the address + Fxh afterward. There is no need to shift the address to make space for the command because the command is already transmitted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pls add
(0xFF<<24)
in parenthesis so that is clear that the OR is the last thing