Add spiflash example project

This commit is contained in:
Clifford Wolf 2017-07-29 16:01:39 +02:00
parent 4ce36a87d1
commit 098829e579
9 changed files with 454 additions and 0 deletions

11
spiflash/.gitignore vendored Normal file
View File

@ -0,0 +1,11 @@
/testbench.vcd
/testbench.vvp
/firmware.elf
/firmware_vma.elf
/firmware.hex
/firmware.bin
/design.asc
/design.bin
/design.blif
/design.log
/design.rpt

40
spiflash/Makefile Normal file
View File

@ -0,0 +1,40 @@
test: testbench.vvp firmware.hex
vvp -N $<
prog: design.bin firmware.bin
iceprog design.bin
iceprog -o 1M firmware.bin
testbench.vvp: spiflash.v spimemio.v testbench.v top.v ../picorv32.v
iverilog -s testbench -o $@ $^
firmware.elf: firmware.s
riscv32-unknown-elf-gcc -c -o firmware.elf firmware.s
firmware_vma.elf: firmware.elf
riscv32-unknown-elf-objcopy --change-section-vma .text=0x00100000 firmware.elf firmware_vma.elf
firmware.hex: firmware_vma.elf
riscv32-unknown-elf-objcopy -O verilog firmware_vma.elf firmware.hex
firmware.bin: firmware.elf
riscv32-unknown-elf-objcopy -O binary firmware.elf firmware.bin
design.blif: spimemio.v top.v ../picorv32.v
yosys -ql design.log -p 'synth_ice40 -top top -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:
rm -f testbench.vvp testbench.vcd
rm -f firmware.elf firmware_vma.elf firmware.hex firmware.bin
rm -f design.blif design.log design.asc design.rpt design.bin
.PHONY: test prog clean

18
spiflash/README Normal file
View File

@ -0,0 +1,18 @@
PicoRV32 SPI-Flash Demo
=======================
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.
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.
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.

8
spiflash/firmware.s Normal file
View File

@ -0,0 +1,8 @@
start:
li x1,0xc0000000
sw x0,0(x0)
loop: lw x2,0(x0)
addi x2,x2,1
sw x2,0(x1)
sw x2,0(x0)
j loop

81
spiflash/pinout.pcf Normal file
View File

@ -0,0 +1,81 @@
# Pinout for the iCE40-HX8K Breakout Board
set_io clk J3
set_io spi_cs R12
set_io spi_sclk R11
set_io spi_miso P11
set_io spi_mosi P12
# 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

105
spiflash/spiflash.v Normal file
View File

@ -0,0 +1,105 @@
module spiflash (
input spi_cs,
output reg spi_miso,
input spi_mosi,
input spi_sclk
);
localparam verbose = 1;
reg [7:0] buffer;
integer bitcount = 0;
integer bytecount = 0;
reg [7:0] spi_cmd;
reg [23:0] spi_addr;
reg [7:0] spi_in;
reg [7:0] spi_out;
reg spi_io_vld;
reg powered_up = 0;
reg in_xfer = 0;
// 16 MB (128Mb) Flash
reg [7:0] memory [0:16*1024*1024-1];
initial begin
$readmemh("firmware.hex", memory);
end
task spi_action;
begin
if (verbose) begin
if (bytecount == 1)
$write("<SPI>");
$write("<SPI:%02x", buffer);
spi_in = buffer;
end
if (bytecount == 1) begin
spi_cmd = buffer;
if (spi_cmd == 8'hAB)
powered_up = 1;
if (spi_cmd == 8'hB9)
powered_up = 0;
end
if (powered_up && spi_cmd == 'h03) begin
if (bytecount == 2)
spi_addr[23:16] = buffer;
if (bytecount == 3)
spi_addr[15:8] = buffer;
if (bytecount == 4)
spi_addr[7:0] = buffer;
if (bytecount >= 4) begin
buffer = memory[spi_addr];
spi_addr = spi_addr + 1;
end
end
if (verbose) begin
$write(":%02x>", buffer);
spi_out = buffer;
spi_io_vld = 1;
$fflush;
end
end
endtask
always @(spi_cs) begin
if (spi_cs) begin
if (verbose && in_xfer) begin
$display("");
$fflush;
end
buffer = 0;
in_xfer = 0;
bitcount = 0;
bytecount = 0;
spi_miso = 0;
end
end
always @(spi_cs, spi_sclk) begin
spi_io_vld = 0;
if (!spi_cs && !spi_sclk) begin
spi_miso = buffer[7];
end
end
always @(posedge spi_sclk) begin
if (!spi_cs) begin
buffer = {buffer, spi_mosi};
bitcount = bitcount + 1;
if (bitcount == 8) begin
in_xfer = 1;
bitcount = 0;
bytecount = bytecount + 1;
spi_action;
end
end
end
endmodule

65
spiflash/spimemio.v Normal file
View File

@ -0,0 +1,65 @@
module spimemio (
input clk, resetn,
input valid,
output reg ready,
input [23:0] addr,
output reg [31:0] rdata,
output reg spi_cs,
output reg spi_sclk,
output reg spi_mosi,
input spi_miso
);
reg [23:0] addr_q;
reg addr_q_vld;
reg [31:0] buffer;
reg [6:0] xfer_cnt;
reg xfer_wait;
always @(posedge clk) begin
ready <= 0;
if (!resetn) begin
spi_cs <= 1;
spi_sclk <= 1;
xfer_cnt <= 8;
buffer <= 8'hAB << 24;
addr_q_vld <= 0;
xfer_wait <= 0;
end else
if (xfer_cnt) begin
if (spi_cs) begin
spi_cs <= 0;
end else
if (spi_sclk) begin
spi_sclk <= 0;
spi_mosi <= buffer[31];
end else begin
spi_sclk <= 1;
buffer <= {buffer, spi_miso};
xfer_cnt <= xfer_cnt - 1;
end
end else
if (xfer_wait) begin
ready <= 1;
rdata <= {buffer[7:0], buffer[15:8], buffer[23:16], buffer[31:24]};
xfer_wait <= 0;
end else
if (valid && !ready) begin
if (addr_q_vld && addr_q == addr) begin
addr_q <= addr + 4;
addr_q_vld <= 1;
xfer_cnt <= 32;
xfer_wait <= 1;
end else begin
spi_cs <= 1;
buffer <= {8'h 03, addr};
addr_q <= addr + 4;
addr_q_vld <= 1;
xfer_cnt <= 64;
xfer_wait <= 1;
end
end
end
endmodule

37
spiflash/testbench.v Normal file
View File

@ -0,0 +1,37 @@
module testbench;
reg clk = 1;
always #5 clk = ~clk;
initial begin
$dumpfile("testbench.vcd");
$dumpvars(0, testbench);
repeat (10000) @(posedge clk);
$display("<END>");
$finish;
end
wire [31:0] gpio_i = 0;
wire [31:0] gpio_o;
wire spi_cs;
wire spi_sclk;
wire spi_mosi;
wire spi_miso;
top uut (
.clk (clk ),
.gpio_i (gpio_i ),
.gpio_o (gpio_o ),
.spi_cs (spi_cs ),
.spi_sclk(spi_sclk),
.spi_mosi(spi_mosi),
.spi_miso(spi_miso)
);
spiflash spiflash (
.spi_cs (spi_cs ),
.spi_sclk(spi_sclk),
.spi_mosi(spi_mosi),
.spi_miso(spi_miso)
);
endmodule

89
spiflash/top.v Normal file
View File

@ -0,0 +1,89 @@
module top (
input clk,
output trap,
input [31:0] gpio_i,
output reg [31:0] gpio_o,
output spi_cs,
output spi_sclk,
output spi_mosi,
input spi_miso
);
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
wire mem_valid;
wire mem_instr;
reg mem_ready;
wire [31:0] mem_addr;
wire [31:0] mem_wdata;
wire [3:0] mem_wstrb;
reg [31:0] mem_rdata;
wire spimem_ready;
wire [31:0] spimem_rdata;
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_addr (mem_addr ),
.mem_wdata (mem_wdata ),
.mem_wstrb (mem_wstrb ),
.mem_rdata (spimem_ready ? spimem_rdata : mem_rdata )
);
spimemio spimemio (
.clk(clk),
.resetn(resetn),
.valid (mem_valid && mem_addr[31:30] == 2'b10),
.ready (spimem_ready),
.addr (mem_addr[23:0]),
.rdata (spimem_rdata),
.spi_cs (spi_cs ),
.spi_sclk (spi_sclk),
.spi_mosi (spi_mosi),
.spi_miso (spi_miso)
);
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
end
end
endmodule