diff options
Diffstat (limited to 'picorv32/picosoc/firmware.c')
-rw-r--r-- | picorv32/picosoc/firmware.c | 770 |
1 files changed, 770 insertions, 0 deletions
diff --git a/picorv32/picosoc/firmware.c b/picorv32/picosoc/firmware.c new file mode 100644 index 0000000..aadf31b --- /dev/null +++ b/picorv32/picosoc/firmware.c @@ -0,0 +1,770 @@ +/* + * PicoSoC - A simple example SoC using PicoRV32 + * + * Copyright (C) 2017 Claire Xenia Wolf <claire@yosyshq.com> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include <stdint.h> +#include <stdbool.h> + +#ifdef ICEBREAKER +# define MEM_TOTAL 0x20000 /* 128 KB */ +#elif HX8KDEMO +# define MEM_TOTAL 0x200 /* 2 KB */ +#else +# error "Set -DICEBREAKER or -DHX8KDEMO when compiling firmware.c" +#endif + +// a pointer to this is a null pointer, but the compiler does not +// know that because "sram" is a linker symbol from sections.lds. +extern uint32_t sram; + +#define reg_spictrl (*(volatile uint32_t*)0x02000000) +#define reg_uart_clkdiv (*(volatile uint32_t*)0x02000004) +#define reg_uart_data (*(volatile uint32_t*)0x02000008) +#define reg_leds (*(volatile uint32_t*)0x03000000) + +// -------------------------------------------------------- + +extern uint32_t flashio_worker_begin; +extern uint32_t flashio_worker_end; + +void flashio(uint8_t *data, int len, uint8_t wrencmd) +{ + uint32_t func[&flashio_worker_end - &flashio_worker_begin]; + + uint32_t *src_ptr = &flashio_worker_begin; + uint32_t *dst_ptr = func; + + while (src_ptr != &flashio_worker_end) + *(dst_ptr++) = *(src_ptr++); + + ((void(*)(uint8_t*, uint32_t, uint32_t))func)(data, len, wrencmd); +} + +#ifdef HX8KDEMO +void set_flash_qspi_flag() +{ + uint8_t buffer[8]; + uint32_t addr_cr1v = 0x800002; + + // Read Any Register (RDAR 65h) + buffer[0] = 0x65; + buffer[1] = addr_cr1v >> 16; + buffer[2] = addr_cr1v >> 8; + buffer[3] = addr_cr1v; + buffer[4] = 0; // dummy + buffer[5] = 0; // rdata + flashio(buffer, 6, 0); + uint8_t cr1v = buffer[5]; + + // Write Enable (WREN 06h) + Write Any Register (WRAR 71h) + buffer[0] = 0x71; + buffer[1] = addr_cr1v >> 16; + buffer[2] = addr_cr1v >> 8; + buffer[3] = addr_cr1v; + buffer[4] = cr1v | 2; // Enable QSPI + flashio(buffer, 5, 0x06); +} + +void set_flash_latency(uint8_t value) +{ + reg_spictrl = (reg_spictrl & ~0x007f0000) | ((value & 15) << 16); + + uint32_t addr = 0x800004; + uint8_t buffer_wr[5] = {0x71, addr >> 16, addr >> 8, addr, 0x70 | value}; + flashio(buffer_wr, 5, 0x06); +} + +void set_flash_mode_spi() +{ + reg_spictrl = (reg_spictrl & ~0x00700000) | 0x00000000; +} + +void set_flash_mode_dual() +{ + reg_spictrl = (reg_spictrl & ~0x00700000) | 0x00400000; +} + +void set_flash_mode_quad() +{ + reg_spictrl = (reg_spictrl & ~0x00700000) | 0x00200000; +} + +void set_flash_mode_qddr() +{ + reg_spictrl = (reg_spictrl & ~0x00700000) | 0x00600000; +} +#endif + +#ifdef ICEBREAKER +void set_flash_qspi_flag() +{ + uint8_t buffer[8]; + + // Read Configuration Registers (RDCR1 35h) + buffer[0] = 0x35; + buffer[1] = 0x00; // rdata + flashio(buffer, 2, 0); + uint8_t sr2 = buffer[1]; + + // Write Enable Volatile (50h) + Write Status Register 2 (31h) + buffer[0] = 0x31; + buffer[1] = sr2 | 2; // Enable QSPI + flashio(buffer, 2, 0x50); +} + +void set_flash_mode_spi() +{ + reg_spictrl = (reg_spictrl & ~0x007f0000) | 0x00000000; +} + +void set_flash_mode_dual() +{ + reg_spictrl = (reg_spictrl & ~0x007f0000) | 0x00400000; +} + +void set_flash_mode_quad() +{ + reg_spictrl = (reg_spictrl & ~0x007f0000) | 0x00240000; +} + +void set_flash_mode_qddr() +{ + reg_spictrl = (reg_spictrl & ~0x007f0000) | 0x00670000; +} + +void enable_flash_crm() +{ + reg_spictrl |= 0x00100000; +} +#endif + +// -------------------------------------------------------- + +void putchar(char c) +{ + if (c == '\n') + putchar('\r'); + reg_uart_data = c; +} + +void print(const char *p) +{ + while (*p) + putchar(*(p++)); +} + +void print_hex(uint32_t v, int digits) +{ + for (int i = 7; i >= 0; i--) { + char c = "0123456789abcdef"[(v >> (4*i)) & 15]; + if (c == '0' && i >= digits) continue; + putchar(c); + digits = i; + } +} + +void print_dec(uint32_t v) +{ + if (v >= 1000) { + print(">=1000"); + return; + } + + if (v >= 900) { putchar('9'); v -= 900; } + else if (v >= 800) { putchar('8'); v -= 800; } + else if (v >= 700) { putchar('7'); v -= 700; } + else if (v >= 600) { putchar('6'); v -= 600; } + else if (v >= 500) { putchar('5'); v -= 500; } + else if (v >= 400) { putchar('4'); v -= 400; } + else if (v >= 300) { putchar('3'); v -= 300; } + else if (v >= 200) { putchar('2'); v -= 200; } + else if (v >= 100) { putchar('1'); v -= 100; } + + if (v >= 90) { putchar('9'); v -= 90; } + else if (v >= 80) { putchar('8'); v -= 80; } + else if (v >= 70) { putchar('7'); v -= 70; } + else if (v >= 60) { putchar('6'); v -= 60; } + else if (v >= 50) { putchar('5'); v -= 50; } + else if (v >= 40) { putchar('4'); v -= 40; } + else if (v >= 30) { putchar('3'); v -= 30; } + else if (v >= 20) { putchar('2'); v -= 20; } + else if (v >= 10) { putchar('1'); v -= 10; } + + if (v >= 9) { putchar('9'); v -= 9; } + else if (v >= 8) { putchar('8'); v -= 8; } + else if (v >= 7) { putchar('7'); v -= 7; } + else if (v >= 6) { putchar('6'); v -= 6; } + else if (v >= 5) { putchar('5'); v -= 5; } + else if (v >= 4) { putchar('4'); v -= 4; } + else if (v >= 3) { putchar('3'); v -= 3; } + else if (v >= 2) { putchar('2'); v -= 2; } + else if (v >= 1) { putchar('1'); v -= 1; } + else putchar('0'); +} + +char getchar_prompt(char *prompt) +{ + int32_t c = -1; + + uint32_t cycles_begin, cycles_now, cycles; + __asm__ volatile ("rdcycle %0" : "=r"(cycles_begin)); + + reg_leds = ~0; + + if (prompt) + print(prompt); + + while (c == -1) { + __asm__ volatile ("rdcycle %0" : "=r"(cycles_now)); + cycles = cycles_now - cycles_begin; + if (cycles > 12000000) { + if (prompt) + print(prompt); + cycles_begin = cycles_now; + reg_leds = ~reg_leds; + } + c = reg_uart_data; + } + + reg_leds = 0; + return c; +} + +char getchar() +{ + return getchar_prompt(0); +} + +void cmd_print_spi_state() +{ + print("SPI State:\n"); + + print(" LATENCY "); + print_dec((reg_spictrl >> 16) & 15); + print("\n"); + + print(" DDR "); + if ((reg_spictrl & (1 << 22)) != 0) + print("ON\n"); + else + print("OFF\n"); + + print(" QSPI "); + if ((reg_spictrl & (1 << 21)) != 0) + print("ON\n"); + else + print("OFF\n"); + + print(" CRM "); + if ((reg_spictrl & (1 << 20)) != 0) + print("ON\n"); + else + print("OFF\n"); +} + +uint32_t xorshift32(uint32_t *state) +{ + /* Algorithm "xor" from p. 4 of Marsaglia, "Xorshift RNGs" */ + uint32_t x = *state; + x ^= x << 13; + x ^= x >> 17; + x ^= x << 5; + *state = x; + + return x; +} + +void cmd_memtest() +{ + int cyc_count = 5; + int stride = 256; + uint32_t state; + + volatile uint32_t *base_word = (uint32_t *) 0; + volatile uint8_t *base_byte = (uint8_t *) 0; + + print("Running memtest "); + + // Walk in stride increments, word access + for (int i = 1; i <= cyc_count; i++) { + state = i; + + for (int word = 0; word < MEM_TOTAL / sizeof(int); word += stride) { + *(base_word + word) = xorshift32(&state); + } + + state = i; + + for (int word = 0; word < MEM_TOTAL / sizeof(int); word += stride) { + if (*(base_word + word) != xorshift32(&state)) { + print(" ***FAILED WORD*** at "); + print_hex(4*word, 4); + print("\n"); + return; + } + } + + print("."); + } + + // Byte access + for (int byte = 0; byte < 128; byte++) { + *(base_byte + byte) = (uint8_t) byte; + } + + for (int byte = 0; byte < 128; byte++) { + if (*(base_byte + byte) != (uint8_t) byte) { + print(" ***FAILED BYTE*** at "); + print_hex(byte, 4); + print("\n"); + return; + } + } + + print(" passed\n"); +} + +// -------------------------------------------------------- + +void cmd_read_flash_id() +{ + uint8_t buffer[17] = { 0x9F, /* zeros */ }; + flashio(buffer, 17, 0); + + for (int i = 1; i <= 16; i++) { + putchar(' '); + print_hex(buffer[i], 2); + } + putchar('\n'); +} + +// -------------------------------------------------------- + +#ifdef HX8KDEMO +uint8_t cmd_read_flash_regs_print(uint32_t addr, const char *name) +{ + set_flash_latency(8); + + uint8_t buffer[6] = {0x65, addr >> 16, addr >> 8, addr, 0, 0}; + flashio(buffer, 6, 0); + + print("0x"); + print_hex(addr, 6); + print(" "); + print(name); + print(" 0x"); + print_hex(buffer[5], 2); + print("\n"); + + return buffer[5]; +} + +void cmd_read_flash_regs() +{ + print("\n"); + uint8_t sr1v = cmd_read_flash_regs_print(0x800000, "SR1V"); + uint8_t sr2v = cmd_read_flash_regs_print(0x800001, "SR2V"); + uint8_t cr1v = cmd_read_flash_regs_print(0x800002, "CR1V"); + uint8_t cr2v = cmd_read_flash_regs_print(0x800003, "CR2V"); + uint8_t cr3v = cmd_read_flash_regs_print(0x800004, "CR3V"); + uint8_t vdlp = cmd_read_flash_regs_print(0x800005, "VDLP"); +} +#endif + +#ifdef ICEBREAKER +uint8_t cmd_read_flash_reg(uint8_t cmd) +{ + uint8_t buffer[2] = {cmd, 0}; + flashio(buffer, 2, 0); + return buffer[1]; +} + +void print_reg_bit(int val, const char *name) +{ + for (int i = 0; i < 12; i++) { + if (*name == 0) + putchar(' '); + else + putchar(*(name++)); + } + + putchar(val ? '1' : '0'); + putchar('\n'); +} + +void cmd_read_flash_regs() +{ + putchar('\n'); + + uint8_t sr1 = cmd_read_flash_reg(0x05); + uint8_t sr2 = cmd_read_flash_reg(0x35); + uint8_t sr3 = cmd_read_flash_reg(0x15); + + print_reg_bit(sr1 & 0x01, "S0 (BUSY)"); + print_reg_bit(sr1 & 0x02, "S1 (WEL)"); + print_reg_bit(sr1 & 0x04, "S2 (BP0)"); + print_reg_bit(sr1 & 0x08, "S3 (BP1)"); + print_reg_bit(sr1 & 0x10, "S4 (BP2)"); + print_reg_bit(sr1 & 0x20, "S5 (TB)"); + print_reg_bit(sr1 & 0x40, "S6 (SEC)"); + print_reg_bit(sr1 & 0x80, "S7 (SRP)"); + putchar('\n'); + + print_reg_bit(sr2 & 0x01, "S8 (SRL)"); + print_reg_bit(sr2 & 0x02, "S9 (QE)"); + print_reg_bit(sr2 & 0x04, "S10 ----"); + print_reg_bit(sr2 & 0x08, "S11 (LB1)"); + print_reg_bit(sr2 & 0x10, "S12 (LB2)"); + print_reg_bit(sr2 & 0x20, "S13 (LB3)"); + print_reg_bit(sr2 & 0x40, "S14 (CMP)"); + print_reg_bit(sr2 & 0x80, "S15 (SUS)"); + putchar('\n'); + + print_reg_bit(sr3 & 0x01, "S16 ----"); + print_reg_bit(sr3 & 0x02, "S17 ----"); + print_reg_bit(sr3 & 0x04, "S18 (WPS)"); + print_reg_bit(sr3 & 0x08, "S19 ----"); + print_reg_bit(sr3 & 0x10, "S20 ----"); + print_reg_bit(sr3 & 0x20, "S21 (DRV0)"); + print_reg_bit(sr3 & 0x40, "S22 (DRV1)"); + print_reg_bit(sr3 & 0x80, "S23 (HOLD)"); + putchar('\n'); +} +#endif + +// -------------------------------------------------------- + +uint32_t cmd_benchmark(bool verbose, uint32_t *instns_p) +{ + uint8_t data[256]; + uint32_t *words = (void*)data; + + uint32_t x32 = 314159265; + + uint32_t cycles_begin, cycles_end; + uint32_t instns_begin, instns_end; + __asm__ volatile ("rdcycle %0" : "=r"(cycles_begin)); + __asm__ volatile ("rdinstret %0" : "=r"(instns_begin)); + + for (int i = 0; i < 20; i++) + { + for (int k = 0; k < 256; k++) + { + x32 ^= x32 << 13; + x32 ^= x32 >> 17; + x32 ^= x32 << 5; + data[k] = x32; + } + + for (int k = 0, p = 0; k < 256; k++) + { + if (data[k]) + data[p++] = k; + } + + for (int k = 0, p = 0; k < 64; k++) + { + x32 = x32 ^ words[k]; + } + } + + __asm__ volatile ("rdcycle %0" : "=r"(cycles_end)); + __asm__ volatile ("rdinstret %0" : "=r"(instns_end)); + + if (verbose) + { + print("Cycles: 0x"); + print_hex(cycles_end - cycles_begin, 8); + putchar('\n'); + + print("Instns: 0x"); + print_hex(instns_end - instns_begin, 8); + putchar('\n'); + + print("Chksum: 0x"); + print_hex(x32, 8); + putchar('\n'); + } + + if (instns_p) + *instns_p = instns_end - instns_begin; + + return cycles_end - cycles_begin; +} + +// -------------------------------------------------------- + +#ifdef HX8KDEMO +void cmd_benchmark_all() +{ + uint32_t instns = 0; + + print("default "); + reg_spictrl = (reg_spictrl & ~0x00700000) | 0x00000000; + print(": "); + print_hex(cmd_benchmark(false, &instns), 8); + putchar('\n'); + + for (int i = 8; i > 0; i--) + { + print("dspi-"); + print_dec(i); + print(" "); + + set_flash_latency(i); + reg_spictrl = (reg_spictrl & ~0x00700000) | 0x00400000; + + print(": "); + print_hex(cmd_benchmark(false, &instns), 8); + putchar('\n'); + } + + for (int i = 8; i > 0; i--) + { + print("dspi-crm-"); + print_dec(i); + print(" "); + + set_flash_latency(i); + reg_spictrl = (reg_spictrl & ~0x00700000) | 0x00500000; + + print(": "); + print_hex(cmd_benchmark(false, &instns), 8); + putchar('\n'); + } + + for (int i = 8; i > 0; i--) + { + print("qspi-"); + print_dec(i); + print(" "); + + set_flash_latency(i); + reg_spictrl = (reg_spictrl & ~0x00700000) | 0x00200000; + + print(": "); + print_hex(cmd_benchmark(false, &instns), 8); + putchar('\n'); + } + + for (int i = 8; i > 0; i--) + { + print("qspi-crm-"); + print_dec(i); + print(" "); + + set_flash_latency(i); + reg_spictrl = (reg_spictrl & ~0x00700000) | 0x00300000; + + print(": "); + print_hex(cmd_benchmark(false, &instns), 8); + putchar('\n'); + } + + for (int i = 8; i > 0; i--) + { + print("qspi-ddr-"); + print_dec(i); + print(" "); + + set_flash_latency(i); + reg_spictrl = (reg_spictrl & ~0x00700000) | 0x00600000; + + print(": "); + print_hex(cmd_benchmark(false, &instns), 8); + putchar('\n'); + } + + for (int i = 8; i > 0; i--) + { + print("qspi-ddr-crm-"); + print_dec(i); + print(" "); + + set_flash_latency(i); + reg_spictrl = (reg_spictrl & ~0x00700000) | 0x00700000; + + print(": "); + print_hex(cmd_benchmark(false, &instns), 8); + putchar('\n'); + } + + print("instns : "); + print_hex(instns, 8); + putchar('\n'); +} +#endif + +#ifdef ICEBREAKER +void cmd_benchmark_all() +{ + uint32_t instns = 0; + + print("default "); + set_flash_mode_spi(); + print_hex(cmd_benchmark(false, &instns), 8); + putchar('\n'); + + print("dual "); + set_flash_mode_dual(); + print_hex(cmd_benchmark(false, &instns), 8); + putchar('\n'); + + // print("dual-crm "); + // enable_flash_crm(); + // print_hex(cmd_benchmark(false, &instns), 8); + // putchar('\n'); + + print("quad "); + set_flash_mode_quad(); + print_hex(cmd_benchmark(false, &instns), 8); + putchar('\n'); + + print("quad-crm "); + enable_flash_crm(); + print_hex(cmd_benchmark(false, &instns), 8); + putchar('\n'); + + print("qddr "); + set_flash_mode_qddr(); + print_hex(cmd_benchmark(false, &instns), 8); + putchar('\n'); + + print("qddr-crm "); + enable_flash_crm(); + print_hex(cmd_benchmark(false, &instns), 8); + putchar('\n'); + +} +#endif + +void cmd_echo() +{ + print("Return to menu by sending '!'\n\n"); + char c; + while ((c = getchar()) != '!') + putchar(c); +} + +// -------------------------------------------------------- + +void main() +{ + reg_leds = 31; + reg_uart_clkdiv = 104; + print("Booting..\n"); + + reg_leds = 63; + set_flash_qspi_flag(); + + reg_leds = 127; + while (getchar_prompt("Press ENTER to continue..\n") != '\r') { /* wait */ } + + print("\n"); + print(" ____ _ ____ ____\n"); + print(" | _ \\(_) ___ ___/ ___| ___ / ___|\n"); + print(" | |_) | |/ __/ _ \\___ \\ / _ \\| |\n"); + print(" | __/| | (_| (_) |__) | (_) | |___\n"); + print(" |_| |_|\\___\\___/____/ \\___/ \\____|\n"); + print("\n"); + + print("Total memory: "); + print_dec(MEM_TOTAL / 1024); + print(" KiB\n"); + print("\n"); + + //cmd_memtest(); // test overwrites bss and data memory + print("\n"); + + cmd_print_spi_state(); + print("\n"); + + while (1) + { + print("\n"); + + print("Select an action:\n"); + print("\n"); + print(" [1] Read SPI Flash ID\n"); + print(" [2] Read SPI Config Regs\n"); + print(" [3] Switch to default mode\n"); + print(" [4] Switch to Dual I/O mode\n"); + print(" [5] Switch to Quad I/O mode\n"); + print(" [6] Switch to Quad DDR mode\n"); + print(" [7] Toggle continuous read mode\n"); + print(" [9] Run simplistic benchmark\n"); + print(" [0] Benchmark all configs\n"); + print(" [M] Run Memtest\n"); + print(" [S] Print SPI state\n"); + print(" [e] Echo UART\n"); + print("\n"); + + for (int rep = 10; rep > 0; rep--) + { + print("Command> "); + char cmd = getchar(); + if (cmd > 32 && cmd < 127) + putchar(cmd); + print("\n"); + + switch (cmd) + { + case '1': + cmd_read_flash_id(); + break; + case '2': + cmd_read_flash_regs(); + break; + case '3': + set_flash_mode_spi(); + break; + case '4': + set_flash_mode_dual(); + break; + case '5': + set_flash_mode_quad(); + break; + case '6': + set_flash_mode_qddr(); + break; + case '7': + reg_spictrl = reg_spictrl ^ 0x00100000; + break; + case '9': + cmd_benchmark(true, 0); + break; + case '0': + cmd_benchmark_all(); + break; + case 'M': + cmd_memtest(); + break; + case 'S': + cmd_print_spi_state(); + break; + case 'e': + cmd_echo(); + break; + default: + continue; + } + + break; + } + } +} |