/* * PicoSoC - A simple example SoC using PicoRV32 * * Copyright (C) 2017 Clifford Wolf * * 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 #include #if !defined(ICEBREAKER) && !defined(HX8KDEMO) # 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 >= 100) { print(">=100"); return; } 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_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"); while (1) { print("\n"); print("\n"); 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"); 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(" [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 'e': cmd_echo(); break; default: continue; } break; } } }