Skip to content

Commit

Permalink
application: sdp: mspi: Hrt Prepare and transfer data
Browse files Browse the repository at this point in the history
  • Loading branch information
mif1-nordic committed Jan 8, 2025
1 parent 516a3f9 commit eaa3e37
Show file tree
Hide file tree
Showing 4 changed files with 384 additions and 213 deletions.
270 changes: 130 additions & 140 deletions applications/sdp/mspi/src/hrt/hrt.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,183 +6,173 @@
#include "hrt.h"
#include <hal/nrf_vpr_csr_vio.h>
#include <hal/nrf_vpr_csr_vtim.h>
#include <drivers/mspi/nrfe_mspi.h>
#include <zephyr/drivers/mspi.h>

#define CLK_FIRST_CYCLE_MULTIPLICATOR (3)

void write_single_by_word(volatile struct hrt_ll_xfer xfer_ll_params)
/** @brief Shift control configuration. */
typedef struct
{
uint16_t dir;
uint16_t out;
nrf_vpr_csr_vio_config_t config;
nrf_vpr_csr_vio_mode_out_t out_mode = {
.mode = NRF_VPR_CSR_VIO_SHIFT_OUTB_TOGGLE,
.frame_width = 1,
};

NRFX_ASSERT(xfer_ll_params.word_size <= MAX_WORD_SIZE);
/* Configuration step */
dir = nrf_vpr_csr_vio_dir_get();
nrf_vpr_csr_vio_dir_set(dir | PIN_DIR_OUT_MASK(VIO(NRFE_MSPI_DQ0_PIN_NUMBER)));
uint8_t shift_count;
nrf_vpr_csr_vio_shift_t out_mode;
uint8_t frame_width;
nrf_vpr_csr_vio_mode_in_t in_mode;
} nrf_vpr_csr_vio_shift_ctrl_t;

NRF_STATIC_INLINE void nrf_vpr_csr_vio_shift_ctrl_buffered_set(nrf_vpr_csr_vio_shift_ctrl_t const * p_shift_ctrl)
{
uint32_t reg = (p_shift_ctrl->shift_count<<VPRCSR_NORDIC_SHIFTCTRLB_SHIFTCNTB_VALUE_Pos) |
(p_shift_ctrl->out_mode<<VPRCSR_NORDIC_SHIFTCTRLB_OUTMODEB_MODE_Pos) |
(p_shift_ctrl->frame_width<<VPRCSR_NORDIC_SHIFTCTRLB_OUTMODEB_FRAMEWIDTH_Pos) |
(p_shift_ctrl->in_mode<<VPRCSR_NORDIC_SHIFTCTRLB_INMODEB_MODE_Pos);

nrf_csr_write(VPRCSR_NORDIC_SHIFTCTRLB, reg);
}

out = nrf_vpr_csr_vio_out_get();
nrf_vpr_csr_vio_out_set(out | PIN_OUT_LOW_MASK(VIO(NRFE_MSPI_DQ0_PIN_NUMBER)));

nrf_vpr_csr_vio_mode_out_set(&out_mode);
nrf_vpr_csr_vio_mode_in_buffered_set(NRF_VPR_CSR_VIO_MODE_IN_CONTINUOUS);

nrf_vpr_csr_vio_config_get(&config);
config.input_sel = false;
nrf_vpr_csr_vio_config_set(&config);

/* Fix position of data if word size < MAX_WORD_SIZE,
* so that leading zeros would not be printed instead of data bits.
*/
if (xfer_ll_params.word_size < MAX_WORD_SIZE) {
for (uint8_t i = 0; i < xfer_ll_params.data_len; i++) {
xfer_ll_params.data_to_send[i] =
xfer_ll_params.data_to_send[i]
<< (MAX_WORD_SIZE - xfer_ll_params.word_size);
}
static void hrt_tx(struct hrt_xfer_data *xfer_data, uint8_t frame_width, bool *counter_running, uint16_t counter_value)
{
if(xfer_data->words == 0)
{
return;
}

/* Counter settings */
nrf_vpr_csr_vtim_count_mode_set(0, NRF_VPR_CSR_VTIM_COUNT_RELOAD);
nrf_vpr_csr_vtim_simple_counter_top_set(0, xfer_ll_params.counter_top);
nrf_vpr_csr_vio_shift_ctrl_t shift_ctrl = {
.shift_count = BITS_IN_WORD / frame_width - 1,
.out_mode = NRF_VPR_CSR_VIO_SHIFT_OUTB_TOGGLE,
.frame_width = frame_width,
.in_mode = NRF_VPR_CSR_VIO_MODE_IN_CONTINUOUS,
};

/* Set number of shifts before OUTB needs to be updated.
* First shift needs to be increased by 1.
*/
nrf_vpr_csr_vio_shift_cnt_out_set(xfer_ll_params.word_size);
nrf_vpr_csr_vio_shift_cnt_out_buffered_set(xfer_ll_params.word_size - 1);

/* Enable CS */
out = nrf_vpr_csr_vio_out_get();
out &= ~PIN_OUT_HIGH_MASK(VIO(NRFE_MSPI_CS0_PIN_NUMBER));
out |= xfer_ll_params.ce_enable_state ? PIN_OUT_HIGH_MASK(VIO(NRFE_MSPI_CS0_PIN_NUMBER))
: PIN_OUT_LOW_MASK(VIO(NRFE_MSPI_CS0_PIN_NUMBER));
nrf_vpr_csr_vio_out_set(out);

/* Start counter */
nrf_vpr_csr_vtim_simple_counter_set(0, CLK_FIRST_CYCLE_MULTIPLICATOR *
xfer_ll_params.counter_top);
nrf_vpr_csr_vio_shift_ctrl_buffered_set(&shift_ctrl);

/* Send data */
for (uint8_t i = 0; i < xfer_ll_params.data_len; i++) {
nrf_vpr_csr_vio_out_buffered_reversed_byte_set(xfer_ll_params.data_to_send[i]);
}
for (uint32_t i = 0; i < xfer_data->words; i++) {

/* Clear all bits, wait until the last word is sent */
nrf_vpr_csr_vio_out_buffered_set(0);
switch (xfer_data->words - i) {
case 1: /* Last transfer */
shift_ctrl.shift_count = xfer_data->last_word_clocks - 1;
nrf_vpr_csr_vio_shift_ctrl_buffered_set(&shift_ctrl);

/* Final configuration */
out_mode.mode = NRF_VPR_CSR_VIO_SHIFT_NONE;
nrf_vpr_csr_vio_mode_out_buffered_set(&out_mode);
nrf_vpr_csr_vio_mode_in_buffered_set(NRF_VPR_CSR_VIO_MODE_IN_CONTINUOUS);
xfer_data->vio_out_set(xfer_data->last_word);
break;
case 2: /* Last but one transfer.*/
shift_ctrl.shift_count = xfer_data->penultimate_word_clocks - 1;
nrf_vpr_csr_vio_shift_ctrl_buffered_set(&shift_ctrl);
default: /* Intentional fallthrough */
xfer_data->vio_out_set(((uint32_t *)xfer_data->data)[i]);
}

/* Disable CS */
if (!xfer_ll_params.ce_hold) {
out = nrf_vpr_csr_vio_out_get();
out &= ~(PIN_OUT_HIGH_MASK(VIO(NRFE_MSPI_CS0_PIN_NUMBER)) |
PIN_OUT_HIGH_MASK(VIO(NRFE_MSPI_SCK_PIN_NUMBER)));
out |= xfer_ll_params.ce_enable_state
? PIN_OUT_LOW_MASK(VIO(NRFE_MSPI_CS0_PIN_NUMBER))
: PIN_OUT_HIGH_MASK(VIO(NRFE_MSPI_CS0_PIN_NUMBER));
nrf_vpr_csr_vio_out_set(out);
if ((i == 0) && (!*counter_running)) {
/* Start counter */
nrf_vpr_csr_vtim_simple_counter_set(0, counter_value);
*counter_running = true;
}
}

/* Stop counter */
nrf_vpr_csr_vtim_count_mode_set(0, NRF_VPR_CSR_VTIM_COUNT_STOP);
}

void write_quad_by_word(volatile struct hrt_ll_xfer xfer_ll_params)
void hrt_write(struct hrt_xfer *xfer_ll_params)
{
uint16_t dir;
uint16_t out;
nrf_vpr_csr_vio_config_t config;
nrf_vpr_csr_vio_mode_out_t out_mode = {
.mode = NRF_VPR_CSR_VIO_SHIFT_OUTB_TOGGLE,
bool counter_running = false;
nrf_vpr_csr_vio_shift_ctrl_t shift_ctrl = {
.shift_count = 1,
.out_mode = NRF_VPR_CSR_VIO_SHIFT_NONE,
.frame_width = 4,
.in_mode = NRF_VPR_CSR_VIO_MODE_IN_CONTINUOUS,
};

nrf_vpr_csr_vio_mode_out_t out_mode = {
.mode = NRF_VPR_CSR_VIO_SHIFT_OUTB_TOGGLE
};

NRFX_ASSERT(xfer_ll_params.word_size % 4 == 0);
NRFX_ASSERT(xfer_ll_params.word_size <= MAX_WORD_SIZE);
/* Configuration step */
dir = nrf_vpr_csr_vio_dir_get();
NRFX_ASSERT((xfer_ll_data.last_word_clocks != 1) || (xfer_ll_data.words == 1))

nrf_vpr_csr_vio_dir_set(dir | PIN_DIR_OUT_MASK(VIO(NRFE_MSPI_DQ0_PIN_NUMBER)) |
PIN_DIR_OUT_MASK(VIO(NRFE_MSPI_DQ1_PIN_NUMBER)) |
PIN_DIR_OUT_MASK(VIO(NRFE_MSPI_DQ2_PIN_NUMBER)) |
PIN_DIR_OUT_MASK(VIO(NRFE_MSPI_DQ3_PIN_NUMBER)));
/* Configure clock and pins */
nrf_vpr_csr_vio_dir_set(xfer_ll_params->tx_direction_mask);

out = nrf_vpr_csr_vio_out_get();
for(uint8_t i=0; i<HRT_FE_MAX; i++) {

nrf_vpr_csr_vio_out_set(out | PIN_OUT_LOW_MASK(VIO(NRFE_MSPI_DQ0_PIN_NUMBER)) |
PIN_OUT_LOW_MASK(VIO(NRFE_MSPI_DQ1_PIN_NUMBER)) |
PIN_OUT_LOW_MASK(VIO(NRFE_MSPI_DQ2_PIN_NUMBER)) |
PIN_OUT_LOW_MASK(VIO(NRFE_MSPI_DQ3_PIN_NUMBER)));

nrf_vpr_csr_vio_mode_out_set(&out_mode);
nrf_vpr_csr_vio_mode_in_buffered_set(NRF_VPR_CSR_VIO_MODE_IN_CONTINUOUS);

nrf_vpr_csr_vio_config_get(&config);
config.input_sel = false;
nrf_vpr_csr_vio_config_set(&config);

/* Fix position of data if word size < MAX_WORD_SIZE,
* so that leading zeros would not be printed instead of data.
*/
if (xfer_ll_params.word_size < MAX_WORD_SIZE) {
for (uint8_t i = 0; i < xfer_ll_params.data_len; i++) {
xfer_ll_params.data_to_send[i] =
xfer_ll_params.data_to_send[i]
<< (MAX_WORD_SIZE - xfer_ll_params.word_size);
if(xfer_ll_params->xfer_data[i].words == 0)
{
break;
}

switch(i) {
case HRT_FE_COMMAND:
out_mode.frame_width = xfer_ll_params->io_mode.command;
break;
case HRT_FE_ADDRESS:
out_mode.frame_width = xfer_ll_params->io_mode.address;
break;
case HRT_FE_DATA:
out_mode.frame_width = xfer_ll_params->io_mode.data;
break;
}
}

/* Counter settings */
nrf_vpr_csr_vtim_count_mode_set(0, NRF_VPR_CSR_VTIM_COUNT_RELOAD);
nrf_vpr_csr_vtim_simple_counter_top_set(0, xfer_ll_params.counter_top);
nrf_vpr_csr_vtim_count_mode_set(0, NRF_VPR_CSR_VTIM_COUNT_RELOAD);
nrf_vpr_csr_vtim_simple_counter_top_set(0, xfer_ll_params->counter_value);
nrf_vpr_csr_vio_mode_in_set(NRF_VPR_CSR_VIO_MODE_IN_CONTINUOUS);

nrf_vpr_csr_vio_mode_out_set(&out_mode);

switch(xfer_ll_params->xfer_data[i].words) {
case 1:
nrf_vpr_csr_vio_shift_cnt_out_set(xfer_ll_params->xfer_data[i].last_word_clocks);
break;
case 2:
nrf_vpr_csr_vio_shift_cnt_out_set(xfer_ll_params->xfer_data[i].penultimate_word_clocks);
break;
default:
nrf_vpr_csr_vio_shift_cnt_out_set(BITS_IN_WORD / out_mode.frame_width);
}

/* Set number of shifts before OUTB needs to be updated.
* First shift needs to be increased by 1.
*/
nrf_vpr_csr_vio_shift_cnt_out_set(xfer_ll_params.word_size / 4);
nrf_vpr_csr_vio_shift_cnt_out_buffered_set(xfer_ll_params.word_size / 4 - 1);
break;
}

/* Enable CS */
out = nrf_vpr_csr_vio_out_get();
out &= ~PIN_OUT_HIGH_MASK(VIO(NRFE_MSPI_CS0_PIN_NUMBER));
out |= xfer_ll_params.ce_enable_state ? PIN_OUT_HIGH_MASK(VIO(NRFE_MSPI_CS0_PIN_NUMBER))
: PIN_OUT_LOW_MASK(VIO(NRFE_MSPI_CS0_PIN_NUMBER));

if (xfer_ll_params->ce_polarity == MSPI_CE_ACTIVE_LOW) {
out = BIT_SET_VALUE(out, xfer_ll_params->ce_vio, VPRCSR_NORDIC_OUT_LOW);
} else {
out = BIT_SET_VALUE(out, xfer_ll_params->ce_vio, VPRCSR_NORDIC_OUT_HIGH);
}
nrf_vpr_csr_vio_out_set(out);

/* Start counter */
nrf_vpr_csr_vtim_simple_counter_set(0, 3 * xfer_ll_params.counter_top);
/* Transfer command */
hrt_tx(&xfer_ll_params->xfer_data[HRT_FE_COMMAND], xfer_ll_params->io_mode.command, &counter_running, xfer_ll_params->counter_value);
/* Transfer address */
hrt_tx(&xfer_ll_params->xfer_data[HRT_FE_ADDRESS], xfer_ll_params->io_mode.address, &counter_running, xfer_ll_params->counter_value);
/* Transfer data */
hrt_tx(&xfer_ll_params->xfer_data[HRT_FE_DATA], xfer_ll_params->io_mode.data, &counter_running, xfer_ll_params->counter_value);

/* Send data */
for (uint8_t i = 0; i < xfer_ll_params.data_len; i++) {
nrf_vpr_csr_vio_out_buffered_reversed_byte_set(xfer_ll_params.data_to_send[i]);
}
if (xfer_ll_params->eliminate_last_pulse) {

/* Clear all bits, wait until the last word is sent */
nrf_vpr_csr_vio_out_buffered_set(0);
/* Wait until the last word is sent */
while(nrf_vpr_csr_vio_shift_cnt_out_get() != 0){}

/* This is a partial solution to surplus clock edge problem in modes 1 and 3.
* This solution works only for counter values above 20.
*/
nrf_vpr_csr_vtim_simple_wait_set(0, false, 0);
}

/* Final configuration */
out_mode.mode = NRF_VPR_CSR_VIO_SHIFT_NONE;
nrf_vpr_csr_vio_mode_out_buffered_set(&out_mode);
nrf_vpr_csr_vio_mode_in_buffered_set(NRF_VPR_CSR_VIO_MODE_IN_CONTINUOUS);
nrf_vpr_csr_vio_shift_ctrl_buffered_set(&shift_ctrl);
nrf_vpr_csr_vio_out_buffered_reversed_word_set(0x00);

/* Stop counter */
nrf_vpr_csr_vtim_count_mode_set(0, NRF_VPR_CSR_VTIM_COUNT_STOP);

/* Disable CS */
if (!xfer_ll_params.ce_hold) {
if (!xfer_ll_params->ce_hold) {

out = nrf_vpr_csr_vio_out_get();
out &= ~(PIN_OUT_HIGH_MASK(VIO(NRFE_MSPI_CS0_PIN_NUMBER)) |
PIN_OUT_HIGH_MASK(VIO(NRFE_MSPI_SCK_PIN_NUMBER)));
out |= xfer_ll_params.ce_enable_state
? PIN_OUT_LOW_MASK(VIO(NRFE_MSPI_CS0_PIN_NUMBER))
: PIN_OUT_HIGH_MASK(VIO(NRFE_MSPI_CS0_PIN_NUMBER));

if (xfer_ll_params->ce_polarity == MSPI_CE_ACTIVE_LOW) {
out = BIT_SET_VALUE(out, xfer_ll_params->ce_vio, VPRCSR_NORDIC_OUT_HIGH);
} else {
out = BIT_SET_VALUE(out, xfer_ll_params->ce_vio, VPRCSR_NORDIC_OUT_LOW);
}
nrf_vpr_csr_vio_out_set(out);
}

/* Stop counter */
nrf_vpr_csr_vtim_count_mode_set(0, NRF_VPR_CSR_VTIM_COUNT_STOP);
}
71 changes: 71 additions & 0 deletions applications/sdp/mspi/src/hrt/hrt.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,80 @@
#define BITS_IN_WORD 32
#define BITS_IN_BYTE 8

enum hrt_frame_element {
HRT_FE_COMMAND,
HRT_FE_ADDRESS,
HRT_FE_DATA,
HRT_FE_MAX
};

/** @brief Structure for holding bus width of individual xfer parts */
struct hrt_xfer_io_mode_cfg {
uint8_t command;
uint8_t address;
uint8_t data;
};

struct hrt_xfer_data {
/** @brief Buffer for RX/TX data */
uint8_t *data;

/** @brief Data length in 4 byte words,
* calculated as CEIL(buffer_length_bits/32).
*/
uint32_t words;

/** @brief Amount of clock pulses for last word.
* Due to hardware limitation, in case when last word clock pulse count is 1,
* the penultimate word has to share its bits with last word,
* for example:
* buffer length = 36bits,
* io_mode = QUAD,
* last_word_clocks would be:(buffer_length%32)/QUAD = 1
* so:
* penultimate_word_clocks = 32-BITS_IN_BYTE
* last_word_clocks = (buffer_length%32)/QUAD + BITS_IN_BYTE
* last_word = penultimate_word>>24 | last_word<<8
*/
uint8_t last_word_clocks;

/** @brief Amount of clock pulses for penultimate word.
* For more info see last_word_clocks.
*/
uint8_t penultimate_word_clocks;

/** @brief Value of last word.
* For more info see last_word_clocks.
*/
uint32_t last_word;

/** @brief Function for writing to buffered out register. */
void (*vio_out_set)(uint32_t value);
};


/** @brief Low level transfer parameters. */
struct hrt_xfer {

/** @brief Data for all transfer parts */
struct hrt_xfer_data xfer_data[HRT_FE_MAX];

/** @brief This xfer parts bus widths */
struct hrt_xfer_io_mode_cfg io_mode;

/** @brief Timer value, used for setting clock frequency
*/
uint16_t counter_value;

/** @brief Index of CE VIO pin */
uint8_t ce_vio;

/** @brief If true chip enable pin will be left active after transfer */
uint8_t ce_hold;

/** @brief Chip enable pin polarity in enabled state. */
enum mspi_ce_polarity ce_polarity;

/** @brief When true clock signal makes 1 transition less.
* It is required for spi modes 1 and 3 due to hardware issue.
*/
Expand Down
Loading

0 comments on commit eaa3e37

Please sign in to comment.