diff --git a/picosoc/.gitignore b/picosoc/.gitignore index e88e84f..905623b 100644 --- a/picosoc/.gitignore +++ b/picosoc/.gitignore @@ -6,8 +6,8 @@ /firmware_vma.elf /firmware.hex /firmware.bin -/design.asc -/design.bin -/design.blif -/design.log -/design.rpt +/hx8kdemo.asc +/hx8kdemo.bin +/hx8kdemo.blif +/hx8kdemo.log +/hx8kdemo.rpt diff --git a/picosoc/Makefile b/picosoc/Makefile index 1436321..8900ecb 100644 --- a/picosoc/Makefile +++ b/picosoc/Makefile @@ -1,4 +1,6 @@ +# ---- Generic Testbenches ---- + testbench: testbench.vvp firmware.hex vvp -N $< @@ -11,10 +13,24 @@ spiflash_tb: spiflash_tb.vvp firmware.hex spiflash_tb.vvp: spiflash.v spiflash_tb.v iverilog -s testbench -o $@ $^ -prog: design.bin firmware.bin - iceprog design.bin +# ---- iCE40 HX8K Breakout Board ---- + +hx8kprog: hx8kdemo.bin firmware.bin + iceprog hx8kdemo.bin iceprog -o 1M firmware.bin +hx8kdemo.blif: hx8kdemo.v spimemio.v picosoc.v ../picorv32.v + yosys -ql hx8kdemo.log -p 'synth_ice40 -top hx8kdemo -blif hx8kdemo.blif' $^ + +hx8kdemo.asc: hx8kdemo.pcf hx8kdemo.blif + arachne-pnr -d 8k -o hx8kdemo.asc -p hx8kdemo.pcf hx8kdemo.blif + +hx8kdemo.bin: hx8kdemo.asc + icetime -d hx8k -c 12 -mtr hx8kdemo.rpt hx8kdemo.asc + icepack hx8kdemo.asc hx8kdemo.bin + +# ---- Example Firmware ---- + firmware.elf: firmware.s riscv32-unknown-elf-gcc -c -o firmware.elf firmware.s @@ -27,20 +43,12 @@ firmware.hex: firmware_vma.elf firmware.bin: firmware.elf riscv32-unknown-elf-objcopy -O binary firmware.elf firmware.bin -design.blif: spimemio.v picosoc.v ../picorv32.v - yosys -ql design.log -p 'synth_ice40 -top picosoc -blif design.blif' $^ - -design.asc: pinout.pcf design.blif - arachne-pnr -d 8k -o design.asc -p pinout.pcf design.blif - -design.bin: design.asc - icetime -d hx8k -c 12 -mtr design.rpt design.asc - icepack design.asc design.bin +# ---- Clean ---- clean: rm -f testbench.vvp testbench.vcd spiflash_tb.vvp spiflash_tb.vcd rm -f firmware.elf firmware_vma.elf firmware.hex firmware.bin - rm -f design.blif design.log design.asc design.rpt design.bin + rm -f hx8kdemo.blif hx8kdemo.log hx8kdemo.asc hx8kdemo.rpt hx8kdemo.bin -.PHONY: testbench spiflash_tb prog clean +.PHONY: testbench spiflash_tb hx8kprog clean diff --git a/picosoc/README.md b/picosoc/README.md index bf3bff6..e70f87c 100644 --- a/picosoc/README.md +++ b/picosoc/README.md @@ -5,23 +5,25 @@ PicoSoC - A simple example SoC using PicoRV32 This is a simple PicoRV32 example design that can run code directly from an SPI flash chip. This example design uses the Lattice iCE40-HX8K Breakout Board. -The flash is mapped to the memory region starting at 0x80000000. The reset -vector is set to 0x80100000, i.e. at the 1MB offset inside the flash memory. +The flash is mapped to the memory region starting at 0x01000000. The reset +vector is set to 0x01100000, i.e. at the 1MB offset inside the flash memory. A small scratchpad memory (default 256 words, i.e. 1 kB) is mapped to address -0x00000000. A simple GPIO controller is mapped to address 0xC0000000. +0x00000000. Run `make test` to run the test bench (and create `testbench.vcd`). Run `make prog` to build the configuration bit-stream and firmware images and upload them to a connected iCE40-HX8K Breakout Board. -| File | Description | -| --------------------------- | --------------------------------------------------------------- | -| [picosoc.v](picosoc.v) | Top-level Verilog module for the design | -| [spimemio.v](spimemio.v) | Memory controller that interfaces to external SPI flash | -| [spiflash.v](spiflash.v) | Simulation model of an SPI flash (used by testbench.v) | -| [testbench.v](testbench.v) | Simple test bench for the design (requires firmware.hex). | -| [firmware.s](firmware.s) | Assembler source for firmware.hex/firmware.bin. | -| [pinout.pcf](pinout.pcf) | Pin constraints for implementation on iCE40-HX8K Breakout Board | +| File | Description | +| ----------------------------- | --------------------------------------------------------------- | +| [picosoc.v](picosoc.v) | Top-level PicoSoC Verilog module | +| [picosoc.v](picosoc.v) | Top-level PicoSoC Verilog module | +| [spimemio.v](spimemio.v) | Memory controller that interfaces to external SPI flash | +| [spiflash.v](spiflash.v) | Simulation model of an SPI flash (used by testbench.v) | +| [testbench.v](testbench.v) | Simple test bench for the design (requires firmware.hex). | +| [firmware.s](firmware.s) | Assembler source for firmware.hex/firmware.bin. | +| [hx8kdemo.v](hx8kdemo.v) | FPGA-based example implementation on iCE40-HX8K Breakout Board | +| [hx8kdemo.pcf](hx8kdemo.pcf) | Pin constraints for implementation on iCE40-HX8K Breakout Board | diff --git a/picosoc/firmware.s b/picosoc/firmware.s index 9b12f0c..69fedcc 100644 --- a/picosoc/firmware.s +++ b/picosoc/firmware.s @@ -23,7 +23,7 @@ li x5,0x00008067 # ret sw x5,80(x0) # setup gpio address in x5 -li x5,0xc0000000 +li x5,0x02000000 sw x0,0(x5) # initial entry point into RAM code diff --git a/picosoc/hx8kdemo.pcf b/picosoc/hx8kdemo.pcf new file mode 100644 index 0000000..c6bf0d9 --- /dev/null +++ b/picosoc/hx8kdemo.pcf @@ -0,0 +1,24 @@ + +# Pinout for the iCE40-HX8K Breakout Board + +set_io clk J3 + +set_io flash_csb R12 +set_io flash_clk R11 +set_io flash_io0 P12 +set_io flash_io1 P11 +set_io flash_io2 T9 # center on J3 +set_io flash_io3 P8 # center on J3 + +set_io ser_tx B12 +set_io ser_rx B10 + +set_io leds[0] B5 # LED0 +set_io leds[1] B4 # LED1 +set_io leds[2] A2 # LED2 +set_io leds[3] A1 # LED3 +set_io leds[4] C5 # LED4 +set_io leds[5] C4 # LED5 +set_io leds[6] B3 # LED6 +set_io leds[7] C3 # LED7 + diff --git a/picosoc/hx8kdemo.v b/picosoc/hx8kdemo.v new file mode 100644 index 0000000..42adcc5 --- /dev/null +++ b/picosoc/hx8kdemo.v @@ -0,0 +1,109 @@ +/* + * 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 hx8kdemo ( + input clk, + + input ser_tx, + input ser_rx, + + output [7:0] leds, + + output flash_csb, + output flash_clk, + inout flash_io0, + inout flash_io1, + inout flash_io2, + inout flash_io3 +); + reg [5:0] reset_cnt = 0; + wire resetn = &reset_cnt; + + always @(posedge clk) begin + reset_cnt <= reset_cnt + !resetn; + end + + wire flash_io0_oe, flash_io0_do, flash_io0_di; + wire flash_io1_oe, flash_io1_do, flash_io1_di; + wire flash_io2_oe, flash_io2_do, flash_io2_di; + wire flash_io3_oe, flash_io3_do, flash_io3_di; + + SB_IO #( + .PIN_TYPE(6'b 1010_01), + .PULLUP(1'b 0) + ) flash_io_buf [3:0] ( + .PACKAGE_PIN({flash_io3, flash_io2, flash_io1, flash_io0}), + .OUTPUT_ENABLE({flash_io3_oe, flash_io2_oe, flash_io1_oe, flash_io0_oe}), + .D_OUT_0({flash_io3_do, flash_io2_do, flash_io1_do, flash_io0_do}), + .D_IN_0({flash_io3_di, flash_io2_di, flash_io1_di, flash_io0_di}) + ); + + wire iomem_valid; + reg iomem_ready; + wire [3:0] iomem_wstrb; + wire [31:0] iomem_addr; + wire [31:0] iomem_wdata; + reg [31:0] iomem_rdata; + + reg [31:0] gpio; + + assign leds = gpio >> 12; + + always @(posedge clk) begin + iomem_ready <= 0; + if (iomem_valid && !iomem_ready && iomem_addr[31:24] == 8'h 02) begin + iomem_ready <= 1; + iomem_rdata <= gpio; + if (iomem_wstrb[0]) gpio[ 7: 0] <= iomem_wdata[ 7: 0]; + if (iomem_wstrb[1]) gpio[15: 8] <= iomem_wdata[15: 8]; + if (iomem_wstrb[2]) gpio[23:16] <= iomem_wdata[23:16]; + if (iomem_wstrb[3]) gpio[31:24] <= iomem_wdata[31:24]; + end + end + + picosoc uut ( + .clk (clk ), + .resetn (resetn ), + + .flash_csb (flash_csb ), + .flash_clk (flash_clk ), + + .flash_io0_oe (flash_io0_oe), + .flash_io1_oe (flash_io1_oe), + .flash_io2_oe (flash_io2_oe), + .flash_io3_oe (flash_io3_oe), + + .flash_io0_do (flash_io0_do), + .flash_io1_do (flash_io1_do), + .flash_io2_do (flash_io2_do), + .flash_io3_do (flash_io3_do), + + .flash_io0_di (flash_io0_di), + .flash_io1_di (flash_io1_di), + .flash_io2_di (flash_io2_di), + .flash_io3_di (flash_io3_di), + + .iomem_valid (iomem_valid ), + .iomem_ready (iomem_ready ), + .iomem_wstrb (iomem_wstrb ), + .iomem_addr (iomem_addr ), + .iomem_wdata (iomem_wdata ), + .iomem_rdata (iomem_rdata ) + ); +endmodule diff --git a/picosoc/picosoc.v b/picosoc/picosoc.v index d1b01dd..6521152 100644 --- a/picosoc/picosoc.v +++ b/picosoc/picosoc.v @@ -19,93 +19,111 @@ module picosoc ( input clk, - output trap, + input resetn, - input [31:0] gpio_i, - output reg [31:0] gpio_o, + output iomem_valid, + input iomem_ready, + output [ 3:0] iomem_wstrb, + output [31:0] iomem_addr, + output [31:0] iomem_wdata, + input [31:0] iomem_rdata, output flash_csb, output flash_clk, - output flash_io0, - input flash_io1, - input flash_io2, - input flash_io3 + + output flash_io0_oe, + output flash_io1_oe, + output flash_io2_oe, + output flash_io3_oe, + + output flash_io0_do, + output flash_io1_do, + output flash_io2_do, + output flash_io3_do, + + input flash_io0_di, + input flash_io1_di, + input flash_io2_di, + input flash_io3_di ); parameter integer MEM_WORDS = 256; parameter [31:0] STACKADDR = (4*MEM_WORDS); // end of memory - parameter [31:0] PROGADDR_RESET = 32'h 8010_0000; // 1 MB into flash - - reg [5:0] reset_cnt = 0; - wire resetn = &reset_cnt; - - always @(posedge clk) begin - reset_cnt <= reset_cnt + !resetn; - end + parameter [31:0] PROGADDR_RESET = 32'h 0110_0000; // 1 MB into flash wire mem_valid; wire mem_instr; - reg mem_ready; + wire mem_ready; wire [31:0] mem_addr; wire [31:0] mem_wdata; wire [3:0] mem_wstrb; - reg [31:0] mem_rdata; + wire [31:0] mem_rdata; wire spimem_ready; wire [31:0] spimem_rdata; + reg ram_ready; + reg [31:0] ram_rdata; + + assign iomem_valid = mem_valid && (mem_addr[31:24] > 8'h 01); + assign iomem_wstrb = mem_wstrb; + assign iomem_addr = mem_addr; + assign iomem_wdata = mem_wdata; + + assign mem_ready = (iomem_valid && iomem_ready) || spimem_ready || ram_ready; + assign mem_rdata = (iomem_valid && iomem_ready) ? iomem_rdata : spimem_ready ? spimem_rdata : ram_ready ? ram_rdata : 32'h xxxx_xxxx; + picorv32 #( .STACKADDR(STACKADDR), .PROGADDR_RESET(PROGADDR_RESET) ) cpu ( .clk (clk ), .resetn (resetn ), - .trap (trap ), .mem_valid (mem_valid ), .mem_instr (mem_instr ), - .mem_ready (mem_ready || spimem_ready), + .mem_ready (mem_ready ), .mem_addr (mem_addr ), .mem_wdata (mem_wdata ), .mem_wstrb (mem_wstrb ), - .mem_rdata (spimem_ready ? spimem_rdata : mem_rdata ) + .mem_rdata (mem_rdata ) ); spimemio spimemio ( .clk(clk), .resetn(resetn), - .valid (mem_valid && mem_addr[31:30] == 2'b10), + .valid (mem_valid && mem_addr[31:24] == 8'h 01), .ready (spimem_ready), .addr (mem_addr[23:0]), .rdata (spimem_rdata), .flash_csb (flash_csb), .flash_clk (flash_clk), - .flash_io0 (flash_io0), - .flash_io1 (flash_io1), - .flash_io2 (flash_io2), - .flash_io3 (flash_io3) + + .flash_io0 (flash_io0_do), + .flash_io1 (flash_io1_di), + .flash_io2 (flash_io2_di), + .flash_io3 (flash_io3_di) ); + assign flash_io0_oe = 1; + assign flash_io1_oe = 0; + assign flash_io2_oe = 0; + assign flash_io3_oe = 0; + + assign flash_io1_do = 0; + assign flash_io2_do = 0; + assign flash_io3_do = 0; + reg [31:0] memory [0:MEM_WORDS-1]; always @(posedge clk) begin - mem_ready <= 0; - if (mem_valid && !mem_ready) begin - if (mem_addr < 4*MEM_WORDS) begin - mem_ready <= 1; - mem_rdata <= memory[mem_addr >> 2]; - if (mem_wstrb[0]) memory[mem_addr >> 2][ 7: 0] <= mem_wdata[ 7: 0]; - if (mem_wstrb[1]) memory[mem_addr >> 2][15: 8] <= mem_wdata[15: 8]; - if (mem_wstrb[2]) memory[mem_addr >> 2][23:16] <= mem_wdata[23:16]; - if (mem_wstrb[3]) memory[mem_addr >> 2][31:24] <= mem_wdata[31:24]; - end - if (mem_addr == 32'h c000_0000) begin - mem_ready <= 1; - mem_rdata <= gpio_i; - if (mem_wstrb[0]) gpio_o[ 7: 0] <= mem_wdata[ 7: 0]; - if (mem_wstrb[1]) gpio_o[15: 8] <= mem_wdata[15: 8]; - if (mem_wstrb[2]) gpio_o[23:16] <= mem_wdata[23:16]; - if (mem_wstrb[3]) gpio_o[31:24] <= mem_wdata[31:24]; - end + ram_ready <= 0; + if (mem_valid && !mem_ready && mem_addr[31:24] == 8'h 00) begin + ram_ready <= 1; + ram_rdata <= memory[mem_addr >> 2]; + if (mem_wstrb[0]) memory[mem_addr >> 2][ 7: 0] <= mem_wdata[ 7: 0]; + if (mem_wstrb[1]) memory[mem_addr >> 2][15: 8] <= mem_wdata[15: 8]; + if (mem_wstrb[2]) memory[mem_addr >> 2][23:16] <= mem_wdata[23:16]; + if (mem_wstrb[3]) memory[mem_addr >> 2][31:24] <= mem_wdata[31:24]; end end endmodule diff --git a/picosoc/pinout.pcf b/picosoc/pinout.pcf deleted file mode 100644 index dea0376..0000000 --- a/picosoc/pinout.pcf +++ /dev/null @@ -1,83 +0,0 @@ - -# Pinout for the iCE40-HX8K Breakout Board - -set_io clk J3 - -set_io flash_csb R12 -set_io flash_clk R11 -set_io flash_io0 P12 -set_io flash_io1 P11 -set_io flash_io2 T9 # center on J3 -set_io flash_io3 P8 # center on J3 - -# All the GPIO pins below are connected to pins that are floating -# on the iCE40-HX8K Breakout Board, expect the marked LED pins. - -set_io trap C3 # LED7 - -set_io gpio_o[0] C14 -set_io gpio_o[1] D13 -set_io gpio_o[2] C12 -set_io gpio_o[3] E11 -set_io gpio_o[4] C13 -set_io gpio_o[5] E10 -set_io gpio_o[6] C11 -set_io gpio_o[7] D11 -set_io gpio_o[8] C10 -set_io gpio_o[9] D10 -set_io gpio_o[10] L10 -set_io gpio_o[11] E9 -set_io gpio_o[12] B5 # LED0 -set_io gpio_o[13] B4 # LED1 -set_io gpio_o[14] A2 # LED2 -set_io gpio_o[15] A1 # LED3 -set_io gpio_o[16] C5 # LED4 -set_io gpio_o[17] C4 # LED5 -set_io gpio_o[18] B3 # LED6 -set_io gpio_o[19] D9 -set_io gpio_o[20] F9 -set_io gpio_o[21] D8 -set_io gpio_o[22] D7 -set_io gpio_o[23] D6 -set_io gpio_o[24] E6 -set_io gpio_o[25] D5 -set_io gpio_o[26] D4 -set_io gpio_o[27] E5 -set_io gpio_o[28] D3 -set_io gpio_o[29] M13 -set_io gpio_o[30] M14 -set_io gpio_o[31] L12 - -set_io gpio_i[0] L13 -set_io gpio_i[1] L14 -set_io gpio_i[2] K12 -set_io gpio_i[3] J10 -set_io gpio_i[4] J11 -set_io gpio_i[5] K13 -set_io gpio_i[6] J12 -set_io gpio_i[7] J13 -set_io gpio_i[8] J16 -set_io gpio_i[9] H13 -set_io gpio_i[10] H12 -set_io gpio_i[11] G10 -set_io gpio_i[12] G11 -set_io gpio_i[13] G13 -set_io gpio_i[14] G12 -set_io gpio_i[15] F12 -set_io gpio_i[16] F11 -set_io gpio_i[17] E14 -set_io gpio_i[18] F13 -set_io gpio_i[19] E13 -set_io gpio_i[20] N6 -set_io gpio_i[21] P4 -set_io gpio_i[22] N5 -set_io gpio_i[23] P5 -set_io gpio_i[24] M7 -set_io gpio_i[25] N7 -set_io gpio_i[26] P6 -set_io gpio_i[27] M8 -set_io gpio_i[28] L9 -set_io gpio_i[29] P7 -set_io gpio_i[30] N9 -set_io gpio_i[31] M9 - diff --git a/picosoc/testbench.v b/picosoc/testbench.v index 47cef3e..8d968e0 100644 --- a/picosoc/testbench.v +++ b/picosoc/testbench.v @@ -18,13 +18,20 @@ */ module testbench; - reg clk = 1; - always #5 clk = ~clk; + reg clk; + always #5 clk = (clk === 1'b0); + + reg resetn = 0; initial begin $dumpfile("testbench.vcd"); $dumpvars(0, testbench); + + repeat (100) @(posedge clk); + resetn <= 1; + repeat (100000) @(posedge clk); + $display(""); $display("[TIMEOUT]"); $stop; @@ -35,32 +42,92 @@ module testbench; wire flash_csb; wire flash_clk; + wire flash_io0; wire flash_io1; wire flash_io2; wire flash_io3; - always @(gpio_o) begin - $write("", gpio_o[7:0]); - if (gpio_o == 63) begin + wire flash_io0_oe; + wire flash_io1_oe; + wire flash_io2_oe; + wire flash_io3_oe; + + wire flash_io0_do; + wire flash_io1_do; + wire flash_io2_do; + wire flash_io3_do; + + wire flash_io0_di = flash_io0; + wire flash_io1_di = flash_io1; + wire flash_io2_di = flash_io2; + wire flash_io3_di = flash_io3; + + assign flash_io0 = flash_io0_oe ? flash_io0_do : 1'bz; + assign flash_io1 = flash_io1_oe ? flash_io1_do : 1'bz; + assign flash_io2 = flash_io2_oe ? flash_io2_do : 1'bz; + assign flash_io3 = flash_io3_oe ? flash_io3_do : 1'bz; + + wire iomem_valid; + reg iomem_ready; + wire [3:0] iomem_wstrb; + wire [31:0] iomem_addr; + wire [31:0] iomem_wdata; + reg [31:0] iomem_rdata; + + reg [31:0] gpio; + + always @(posedge clk) begin + iomem_ready <= 0; + if (iomem_valid && !iomem_ready && iomem_addr[31:24] == 8'h 02) begin + iomem_ready <= 1; + iomem_rdata <= gpio; + if (iomem_wstrb[0]) gpio[ 7: 0] <= iomem_wdata[ 7: 0]; + if (iomem_wstrb[1]) gpio[15: 8] <= iomem_wdata[15: 8]; + if (iomem_wstrb[2]) gpio[23:16] <= iomem_wdata[23:16]; + if (iomem_wstrb[3]) gpio[31:24] <= iomem_wdata[31:24]; + end + end + + always @(gpio) begin + $write("", gpio[7:0]); + if (gpio == 63) begin $display("[OK]"); $finish; end - if (gpio_o % 8 == 7) begin + if (gpio % 8 == 7) begin $display(""); end end picosoc uut ( - .clk (clk ), - .gpio_i (gpio_i ), - .gpio_o (gpio_o ), - .flash_csb(flash_csb), - .flash_clk(flash_clk), - .flash_io0(flash_io0), - .flash_io1(flash_io1), - .flash_io2(flash_io2), - .flash_io3(flash_io3) + .clk (clk ), + .resetn (resetn ), + + .flash_csb (flash_csb ), + .flash_clk (flash_clk ), + + .flash_io0_oe (flash_io0_oe), + .flash_io1_oe (flash_io1_oe), + .flash_io2_oe (flash_io2_oe), + .flash_io3_oe (flash_io3_oe), + + .flash_io0_do (flash_io0_do), + .flash_io1_do (flash_io1_do), + .flash_io2_do (flash_io2_do), + .flash_io3_do (flash_io3_do), + + .flash_io0_di (flash_io0_di), + .flash_io1_di (flash_io1_di), + .flash_io2_di (flash_io2_di), + .flash_io3_di (flash_io3_di), + + .iomem_valid (iomem_valid ), + .iomem_ready (iomem_ready ), + .iomem_wstrb (iomem_wstrb ), + .iomem_addr (iomem_addr ), + .iomem_wdata (iomem_wdata ), + .iomem_rdata (iomem_rdata ) ); spiflash spiflash (