diff --git a/picosoc/.gitignore b/picosoc/.gitignore index 1817f7d..08067d3 100644 --- a/picosoc/.gitignore +++ b/picosoc/.gitignore @@ -11,6 +11,7 @@ /hx8kdemo_fw.elf /hx8kdemo_fw.hex /hx8kdemo_fw.bin +/hx8kdemo_sections.lds /icebreaker.asc /icebreaker.bin /icebreaker.json @@ -22,5 +23,7 @@ /icebreaker_fw.elf /icebreaker_fw.hex /icebreaker_fw.bin +/icebreaker_sections.lds /testbench.vcd /cmos.log + diff --git a/picosoc/Makefile b/picosoc/Makefile index aee1195..ab01df4 100644 --- a/picosoc/Makefile +++ b/picosoc/Makefile @@ -5,7 +5,7 @@ hx8ksim: hx8kdemo_tb.vvp hx8kdemo_fw.hex vvp -N $< +firmware=hx8kdemo_fw.hex hx8ksynsim: hx8kdemo_syn_tb.vvp hx8kdemo_fw.hex - vvp -N $< +firmware=hx8kdemo_fw.hex + vvp -N $< +firmware=hx8kdemo_fw.hex hx8kdemo.blif: hx8kdemo.v spimemio.v simpleuart.v picosoc.v ../picorv32.v yosys -ql hx8kdemo.log -p 'synth_ice40 -top hx8kdemo -blif hx8kdemo.blif' $^ @@ -33,8 +33,11 @@ hx8kprog: hx8kdemo.bin hx8kdemo_fw.bin hx8kprog_fw: hx8kdemo_fw.bin iceprog -o 1M hx8kdemo_fw.bin -hx8kdemo_fw.elf: sections.lds start.s firmware.c - riscv32-unknown-elf-gcc -DHX8KDEMO -march=rv32imc -Wl,-Bstatic,-T,sections.lds,--strip-debug -ffreestanding -nostdlib -o hx8kdemo_fw.elf start.s firmware.c +hx8kprog_sections.lds: sections.lds + riscv32-unknown-elf-cpp -P -DHX8KDEMO -o $@ $^ + +hx8kdemo_fw.elf: hx8kdemo_sections.lds start.s firmware.c + riscv32-unknown-elf-gcc -DHX8KDEMO -march=rv32imc -Wl,-Bstatic,-T,hx8kdemo_sections.lds,--strip-debug -ffreestanding -nostdlib -o hx8kdemo_fw.elf start.s firmware.c hx8kdemo_fw.hex: hx8kdemo_fw.elf riscv32-unknown-elf-objcopy -O verilog hx8kdemo_fw.elf hx8kdemo_fw.hex @@ -50,10 +53,10 @@ icebsim: icebreaker_tb.vvp icebreaker_fw.hex icebsynsim: icebreaker_syn_tb.vvp icebreaker_fw.hex vvp -N $< +firmware=icebreaker_fw.hex -icebreaker.json: icebreaker.v spimemio.v simpleuart.v picosoc.v ../picorv32.v +icebreaker.json: icebreaker.v ice40up5k_spram.v spimemio.v simpleuart.v picosoc.v ../picorv32.v yosys -ql icebreaker.log -p 'synth_ice40 -top icebreaker -json icebreaker.json' $^ -icebreaker_tb.vvp: icebreaker_tb.v icebreaker.v spimemio.v simpleuart.v picosoc.v ../picorv32.v spiflash.v +icebreaker_tb.vvp: icebreaker_tb.v icebreaker.v ice40up5k_spram.v spimemio.v simpleuart.v picosoc.v ../picorv32.v spiflash.v iverilog -s testbench -o $@ $^ `yosys-config --datdir/ice40/cells_sim.v` icebreaker_syn_tb.vvp: icebreaker_tb.v icebreaker_syn.v spiflash.v @@ -76,8 +79,11 @@ icebprog: icebreaker.bin icebreaker_fw.bin icebprog_fw: icebreaker_fw.bin iceprog -o 1M icebreaker_fw.bin -icebreaker_fw.elf: sections.lds start.s firmware.c - riscv32-unknown-elf-gcc -DICEBREAKER -march=rv32ic -Wl,-Bstatic,-T,sections.lds,--strip-debug -ffreestanding -nostdlib -o icebreaker_fw.elf start.s firmware.c +icebreaker_sections.lds: sections.lds + riscv32-unknown-elf-cpp -P -DICEBREAKER -o $@ $^ + +icebreaker_fw.elf: icebreaker_sections.lds start.s firmware.c + riscv32-unknown-elf-gcc -DICEBREAKER -march=rv32ic -Wl,-Bstatic,-T,icebreaker_sections.lds,--strip-debug -ffreestanding -nostdlib -o icebreaker_fw.elf start.s firmware.c icebreaker_fw.hex: icebreaker_fw.elf riscv32-unknown-elf-objcopy -O verilog icebreaker_fw.elf icebreaker_fw.hex diff --git a/picosoc/firmware.c b/picosoc/firmware.c index b53fa0e..19de4e0 100644 --- a/picosoc/firmware.c +++ b/picosoc/firmware.c @@ -20,7 +20,11 @@ #include #include -#if !defined(ICEBREAKER) && !defined(HX8KDEMO) +#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 @@ -176,11 +180,21 @@ void print_hex(uint32_t v, int digits) void print_dec(uint32_t v) { - if (v >= 100) { - print(">=100"); + 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; } @@ -236,6 +250,95 @@ 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() @@ -578,36 +681,23 @@ void main() print(" | |_) | |/ __/ _ \\___ \\ / _ \\| |\n"); print(" | __/| | (_| (_) |__) | (_) | |___\n"); print(" |_| |_|\\___\\___/____/ \\___/ \\____|\n"); + print("\n"); + + print("Total memory: "); + print_dec(MEM_TOTAL / 1024); + print(" KiB\n"); + print("\n"); + + cmd_memtest(); + print("\n"); + + cmd_print_spi_state(); + 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"); @@ -619,6 +709,8 @@ void main() 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"); @@ -659,6 +751,12 @@ void main() case '0': cmd_benchmark_all(); break; + case 'M': + cmd_memtest(); + break; + case 'P': + cmd_print_spi_state(); + break; case 'e': cmd_echo(); break; diff --git a/picosoc/ice40up5k_spram.v b/picosoc/ice40up5k_spram.v new file mode 100644 index 0000000..6edb23b --- /dev/null +++ b/picosoc/ice40up5k_spram.v @@ -0,0 +1,91 @@ + +/* + * 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. + * + */ + +module ice40up5k_spram #( + // We current always use the whole SPRAM (128 kB) + parameter integer WORDS = 32768 +) ( + input clk, + input [3:0] wen, + input [21:0] addr, + input [31:0] wdata, + output [31:0] rdata +); + + wire cs_0, cs_1; + wire [31:0] rdata_0, rdata_1; + + assign cs_0 = !addr[14]; + assign cs_1 = addr[14]; + assign rdata = addr[14] ? rdata_1 : rdata_0; + + SB_SPRAM256KA ram00 ( + .ADDRESS(addr[13:0]), + .DATAIN(wdata[15:0]), + .MASKWREN({wen[1], wen[1], wen[0], wen[0]}), + .WREN(wen[1]|wen[0]), + .CHIPSELECT(cs_0), + .CLOCK(clk), + .STANDBY(1'b0), + .SLEEP(1'b0), + .POWEROFF(1'b1), + .DATAOUT(rdata_0[15:0]) + ); + + SB_SPRAM256KA ram01 ( + .ADDRESS(addr[13:0]), + .DATAIN(wdata[31:16]), + .MASKWREN({wen[3], wen[3], wen[2], wen[2]}), + .WREN(wen[3]|wen[2]), + .CHIPSELECT(cs_0), + .CLOCK(clk), + .STANDBY(1'b0), + .SLEEP(1'b0), + .POWEROFF(1'b1), + .DATAOUT(rdata_0[31:16]) + ); + + SB_SPRAM256KA ram10 ( + .ADDRESS(addr[13:0]), + .DATAIN(wdata[15:0]), + .MASKWREN({wen[1], wen[1], wen[0], wen[0]}), + .WREN(wen[1]|wen[0]), + .CHIPSELECT(cs_1), + .CLOCK(clk), + .STANDBY(1'b0), + .SLEEP(1'b0), + .POWEROFF(1'b1), + .DATAOUT(rdata_1[15:0]) + ); + + SB_SPRAM256KA ram11 ( + .ADDRESS(addr[13:0]), + .DATAIN(wdata[31:16]), + .MASKWREN({wen[3], wen[3], wen[2], wen[2]}), + .WREN(wen[3]|wen[2]), + .CHIPSELECT(cs_1), + .CLOCK(clk), + .STANDBY(1'b0), + .SLEEP(1'b0), + .POWEROFF(1'b1), + .DATAOUT(rdata_1[31:16]) + ); + +endmodule diff --git a/picosoc/icebreaker.v b/picosoc/icebreaker.v index ef9b9ac..342f346 100644 --- a/picosoc/icebreaker.v +++ b/picosoc/icebreaker.v @@ -17,6 +17,12 @@ * */ +`ifdef PICOSOC_V +`error "icebreaker.v must be read before icebreaker.v!" +`endif + +`define PICOSOC_MEM ice40up5k_spram + module icebreaker ( input clk, @@ -39,6 +45,8 @@ module icebreaker ( inout flash_io2, inout flash_io3 ); + parameter integer MEM_WORDS = 32768; + reg [5:0] reset_cnt = 0; wire resetn = &reset_cnt; @@ -100,7 +108,8 @@ module icebreaker ( picosoc #( .BARREL_SHIFTER(0), - .ENABLE_MULDIV(0) + .ENABLE_MULDIV(0), + .MEM_WORDS(MEM_WORDS) ) soc ( .clk (clk ), .resetn (resetn ), diff --git a/picosoc/icebreaker_tb.v b/picosoc/icebreaker_tb.v index 2126ca2..209d165 100644 --- a/picosoc/icebreaker_tb.v +++ b/picosoc/icebreaker_tb.v @@ -62,7 +62,12 @@ module testbench; #1 $display("%b", leds); end - icebreaker uut ( + icebreaker #( + // We limit the amount of memory in simulation + // in order to avoid reduce simulation time + // required for intialization of RAM + .MEM_WORDS(256) + ) uut ( .clk (clk ), .led1 (led1 ), .led2 (led2 ), diff --git a/picosoc/picosoc.core b/picosoc/picosoc.core index a6eae08..eb0988a 100644 --- a/picosoc/picosoc.core +++ b/picosoc/picosoc.core @@ -14,10 +14,14 @@ filesets: targets: default: filesets : [picosoc] - parameters : [PICORV32_REGS] + parameters : [PICORV32_REGS, PICOSOC_MEM] parameters: PICORV32_REGS: datatype : str default : picosoc_regs paramtype : vlogdefine + PICOSOC_MEM: + datatype : str + default : picosoc_mem + paramtype : vlogdefine diff --git a/picosoc/picosoc.v b/picosoc/picosoc.v index 353f2ef..9c5981e 100644 --- a/picosoc/picosoc.v +++ b/picosoc/picosoc.v @@ -25,6 +25,14 @@ `define PICORV32_REGS picosoc_regs `endif +`ifndef PICOSOC_MEM +`define PICOSOC_MEM picosoc_mem +`endif + +// this macro can be used to check if the verilog files in your +// design are read in the correct order. +`define PICOSOC_V + module picosoc ( input clk, input resetn, @@ -197,7 +205,9 @@ module picosoc ( always @(posedge clk) ram_ready <= mem_valid && !mem_ready && mem_addr < 4*MEM_WORDS; - picosoc_mem #(.WORDS(MEM_WORDS)) memory ( + `PICOSOC_MEM #( + .WORDS(MEM_WORDS) + ) memory ( .clk(clk), .wen((mem_valid && !mem_ready && mem_addr < 4*MEM_WORDS) ? mem_wstrb : 4'b0), .addr(mem_addr[23:2]), diff --git a/picosoc/sections.lds b/picosoc/sections.lds index 5f74459..f38d813 100644 --- a/picosoc/sections.lds +++ b/picosoc/sections.lds @@ -1,7 +1,15 @@ +#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 + MEMORY { FLASH (rx) : ORIGIN = 0x00100000, LENGTH = 0x400000 /* entire flash, 4 MiB */ - RAM (xrw) : ORIGIN = 0x00000000, LENGTH = 0x000400 /* 1 KB */ + RAM (xrw) : ORIGIN = 0x00000000, LENGTH = MEM_TOTAL } SECTIONS {