Start work on SPI testing framework

This commit is contained in:
Kelvin Ly 2022-04-16 08:39:04 -04:00
parent d6402ff656
commit ad4621cb2e
8 changed files with 372 additions and 2 deletions

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
*-backups/
build/
hostbuild/

View File

@ -23,3 +23,28 @@ pico_enable_stdio_usb(uart_test 1)
pico_enable_stdio_uart(uart_test 0)
pico_add_extra_outputs(uart_test)
add_executable(blink_test
src/blink_test.c
)
target_link_libraries(blink_test pico_stdlib)
pico_enable_stdio_usb(blink_test 1)
pico_enable_stdio_uart(blink_test 0)
pico_add_extra_outputs(blink_test)
add_executable(spi_test
src/spi_test.c
)
target_link_libraries(spi_test
pico_stdlib
hardware_spi
)
pico_enable_stdio_usb(spi_test 1)
pico_enable_stdio_uart(spi_test 0)
pico_add_extra_outputs(spi_test)

View File

@ -3,6 +3,12 @@
#define PICO_FLASH_SIZE_BYTES (16*1024*1024)
#define PICO_DEFAULT_SPI 0
#define PICO_DEFAULT_SPI_SCK_PIN 1
#define PICO_DEFAULT_SPI_TX_PIN 3
#define PICO_DEFAULT_SPI_RX_PIN 0
#define PICO_DEFAULT_SPI_CSN_PIN 2
#include "boards/pico.h"
#undef PICO_BOOT_STAGE2_CHOOSE_W25Q080

10
host/CMakeLists.txt Normal file
View File

@ -0,0 +1,10 @@
cmake_minimum_required(VERSION 3.12)
project(walkie_talkies_host)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
add_executable(spi_parse_test
../src/spi_parser_test.c
)

View File

@ -1,11 +1,17 @@
#include <stdio.h>
#include "pico/stdlib.h"
#define TEST_GPIO 8
int main() {
stdio_init_all();
gpio_init(TEST_GPIO);
gpio_set_dir(TEST_GPIO, GPIO_OUT);
bool val = true;
while (true) {
printf("test!\n");
sleep_ms(250);
gpio_put(TEST_GPIO, val);
val = !val;
sleep_ms(25);
}
return 0;
}

128
src/spi_parser.h Normal file
View File

@ -0,0 +1,128 @@
#ifndef SPI_PARSER_H_
#define SPI_PARSER_H_
#include <stdbool.h>
// probably should include stdint but I'm not sure how that'd
// interact with Pico SDK's stuff, so I'll leave it out
static int maybe_nibble(char c) {
if ((c >= 'a') && (c <= 'f')) {
return c - 'a' + 10;
}
if ((c >= '0') && (c <= '9')) {
return c - '0';
}
return -1;
}
static void spi_register_write(uint16_t address, const uint8_t *buf, uint16_t len);
static void spi_register_read(uint16_t address, uint8_t *buf, uint16_t len);
static void parse_and_execute_cmd(const uint8_t* s, int c_len) {
if (c_len == 0) {
return;
}
int is_write = (s[0] == 'w');
int is_read = (s[0] == 'r');
if (!is_write && !is_read) {
puts("invalid cmd; no r/w");
return;
}
int offset = 1;
uint16_t addr = 0;
int len = 0;
uint8_t buf[64];
for (int i = 0; i < 4 && (i + 1) < c_len; i++) {
int mn = maybe_nibble(s[i + offset]);
if (mn < 0) {
puts("invalid cmd; bad addr");
return;
}
addr = (addr << 4) | (mn & 0xf);
}
offset += 4;
offset += 1; // skip a character, some kind of whitespace is allowed
// variable length entry, read until
int readlen = 0;
int mn = maybe_nibble(s[offset]);
while ((mn >= 0) && (offset < c_len)) {
len = (len << 4) | (mn & 0xf);
++offset;
mn = maybe_nibble(s[offset]);
}
if (len > sizeof(buf)) {
len = sizeof(buf);
}
const uint8_t tohex[] = "0123456789abcdef";
if (is_read) {
// we're good, time to execute
spi_register_read(addr, buf, len);
uint8_t output_str[3*sizeof(buf) + 1];
for (int i = 0; i < len; i++) {
output_str[3*i] = tohex[(buf[i] >> 4) & 0xf];
output_str[3*i + 1] = tohex[buf[i] & 0xf];
output_str[3*i + 2] = ' ';
}
output_str[3*len - 1] = '\n';
output_str[3*len] = 0;
puts(output_str);
return;
}
++offset; // skip whitespace character
if (is_write) {
// parse write data one nibble at a time until it reaches the end
// or len characters have been read
int nibble_count = 0;
int cur_byte = 0;
for (; (nibble_count < 2*len) && (offset < c_len); offset++) {
int mn = maybe_nibble(s[offset]);
if (mn >= 0) {
cur_byte = (cur_byte << 4) | (mn & 0xf);
++nibble_count;
if ((nibble_count % 2) == 0) {
buf[(nibble_count/2) - 1] = cur_byte & 0xff;
}
}
}
if (nibble_count < 2*len) {
puts("parse error: not enough data for write\n");
return;
}
spi_register_write(addr, buf, len);
uint8_t output_s[3*sizeof(buf) + 4 + 9];
output_s[0] = 'w';
output_s[1] = ' ';
for (int i = 0; i < 4; i++) {
output_s[2+i] = tohex[(addr >> (4*(3-i))) & 0xf];
}
output_s[2+4] = ' ';
uint8_t* output_str = output_s + 7;
for (int i = 0; i < len; i++) {
output_str[3*i] = tohex[(buf[i] >> 4) & 0xf];
output_str[3*i + 1] = tohex[buf[i] & 0xf];
output_str[3*i + 2] = ' ';
}
output_str[3*len - 1] = '\n';
output_str[3*len] = 0;
puts(output_s);
return;
}
}
#endif // SPI_PARSER_H_

107
src/spi_parser_test.c Normal file
View File

@ -0,0 +1,107 @@
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include "spi_parser.h"
int reg_written = 0;
int reg_read = 0;
int reg_addr = 0;
int reg_len = 0;
uint8_t reg_buf[64];
static void spi_register_write(
uint16_t address, const uint8_t *buf, uint16_t len) {
reg_written = 1;
reg_addr = address;
reg_len = len;
memcpy(reg_buf, buf, len);
}
static void spi_register_read(uint16_t address, uint8_t *buf, uint16_t len) {
reg_read = 1;
reg_addr = address;
reg_len = len;
}
static void reset_reg() {
reg_written = 0;
reg_read = 0;
reg_addr = 0;
reg_len = 0;
}
struct test_case {
const uint8_t* s;
int s_len;
int is_read;
int is_write;
int addr;
int len;
const uint8_t* expected;
};
static int check_reg(struct test_case* tc) {
if (tc->is_read) {
return reg_read && (reg_addr == tc->addr) && (reg_len == tc->len);
} else if (tc->is_write) {
return reg_written && (reg_addr == tc->addr) && (reg_len == tc->len) && (memcmp(reg_buf, tc->expected, tc->len) == 0);
}
return !reg_read && !reg_written;
}
static void dump_expected(struct test_case* tc) {
if (tc->is_read && !reg_read) {
printf("read bit not set, ");
} else if (tc->is_write && !reg_written) {
printf("write bit not set, ");
} else {
if (reg_read) {
printf("read bit SET, should\'nt be \n");
return;
}
if (reg_written) {
printf("read bit SET, should\'nt be \n");
return;
}
}
if (tc->addr != reg_addr) {
printf("got addr %04x, expected %04x, ", reg_addr, tc->addr);
}
if (tc->len != reg_len) {
printf("got len %04x, expected %04x, ", reg_len, tc->len);
}
printf("\n");
}
int main() {
const uint8_t test0[] = "r0102 1\n";
const uint8_t test1[] = "r1234 1a\n";
const uint8_t test2[] = "wffe7 3 11 22 33\n";
const uint8_t test2_buf[] = {0x11, 0x22, 0x33};
const uint8_t test3[] = "\n";
const uint8_t test4[] = "w1ae7 8 1122 3344 ffee e0 f5\n";
const uint8_t test4_buf[] = {0x11, 0x22, 0x33, 0x44, 0xff, 0xee, 0xe0, 0xf5};
struct test_case cases[] = {
{test0, sizeof(test0), 1, 0, 0x0102, 1, NULL},
{test1, sizeof(test1), 1, 0, 0x1234, 0x1a, NULL},
{test2, sizeof(test2), 0, 1, 0xffe7, 0x3, test2_buf},
{test3, sizeof(test3), 0, 0, 0, 0, NULL},
{test4, sizeof(test4), 0, 1, 0x1ae7, 8, test4_buf},
};
for (int i = 0; i < sizeof(cases)/sizeof(cases[0]); i++) {
reset_reg();
parse_and_execute_cmd(cases[i].s, cases[i].s_len);
if (!check_reg(&cases[i])) {
printf("E: test failed\n");
dump_expected(&cases[i]);
return -1;
} else {
printf("I: test %d passed\n", i);
}
}
return 0;
}

87
src/spi_test.c Normal file
View File

@ -0,0 +1,87 @@
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/spi.h"
#include "spi_parser.h"
#define AT86_RSTN 7
static void cs_select() {
asm volatile("nop \n nop \n nop");
gpio_put(PICO_DEFAULT_SPI_CSN_PIN, 0); // Active low
asm volatile("nop \n nop \n nop");
}
static void cs_deselect() {
asm volatile("nop \n nop \n nop");
gpio_put(PICO_DEFAULT_SPI_CSN_PIN, 1);
asm volatile("nop \n nop \n nop");
}
static void spi_register_write(uint16_t address, const uint8_t *buf, uint16_t len) {
uint8_t cmd[2] = { address >> 8, address };
cmd[0] &= ~(3 << 6); // mask out top two bits
// to set MODE to read
cs_select();
spi_write_blocking(PICO_DEFAULT_SPI, cmd, 2);
spi_write_blocking(PICO_DEFAULT_SPI, buf, len);
cs_deselect();
}
static void spi_register_read(uint16_t address, uint8_t *buf, uint16_t len) {
uint8_t cmd[2] = { address >> 8, address };
cmd[0] &= ~(3 << 6); // mask out top two bits
cmd[0] |= (2 << 6);
// set MODE[0] to set to write
cs_select();
spi_write_blocking(PICO_DEFAULT_SPI, cmd, 2);
spi_read_blocking(PICO_DEFAULT_SPI, 0, buf, len);
cs_deselect();
}
int main() {
stdio_init_all();
// 10 MHz
spi_init(PICO_DEFAULT_SPI, 10*1000*1000);
gpio_set_function(PICO_DEFAULT_SPI_RX_PIN, GPIO_FUNC_SPI);
gpio_set_function(PICO_DEFAULT_SPI_SCK_PIN, GPIO_FUNC_SPI);
gpio_set_function(PICO_DEFAULT_SPI_TX_PIN, GPIO_FUNC_SPI);
gpio_init(PICO_DEFAULT_SPI_CSN_PIN);
gpio_set_dir(PICO_DEFAULT_SPI_CSN_PIN, GPIO_OUT);
gpio_put(PICO_DEFAULT_SPI_CSN_PIN, 1);
gpio_set_dir(AT86_RSTN, GPIO_OUT);
gpio_put(AT86_RSTN, 1);
uint8_t cmd[64];
int cmd_idx = 0;
while (true) {
if (stdio_usb_connected()) {
// UART parse loop
int maybe_ch = getchar_timeout_us(1000);
if (maybe_ch != PICO_ERROR_TIMEOUT) {
if (maybe_ch == 0x0a) {
cmd[cmd_idx] = 0;
putchar(maybe_ch);
// process command
parse_and_execute_cmd(cmd, cmd_idx);
cmd_idx = 0;
} else if (maybe_ch == 0x1b) {
putchar('^');
putchar('\n');
cmd_idx = 0;
} else {
putchar(maybe_ch);
cmd[cmd_idx] = maybe_ch;
if (cmd_idx < sizeof(cmd)-1) {
++cmd_idx;
}
}
}
} else {
sleep_ms(10);
}
}
}