diff --git a/picosoc/.gitignore b/picosoc/.gitignore index 905623b..cef6edf 100644 --- a/picosoc/.gitignore +++ b/picosoc/.gitignore @@ -11,3 +11,6 @@ /hx8kdemo.blif /hx8kdemo.log /hx8kdemo.rpt +/hx8kdemo_syn.v +/hx8kdemo_syn_tb.vvp +/hx8kdemo_tb.vvp diff --git a/picosoc/Makefile b/picosoc/Makefile index 8900ecb..8297a63 100644 --- a/picosoc/Makefile +++ b/picosoc/Makefile @@ -19,9 +19,24 @@ hx8kprog: hx8kdemo.bin firmware.bin iceprog hx8kdemo.bin iceprog -o 1M firmware.bin +hx8ksim: hx8kdemo_tb.vvp firmware.hex + vvp -N $< + +hx8ksynsim: hx8kdemo_syn_tb.vvp firmware.hex + vvp -N $< + hx8kdemo.blif: hx8kdemo.v spimemio.v picosoc.v ../picorv32.v yosys -ql hx8kdemo.log -p 'synth_ice40 -top hx8kdemo -blif hx8kdemo.blif' $^ +hx8kdemo_tb.vvp: hx8kdemo_tb.v hx8kdemo.v spimemio.v picosoc.v ../picorv32.v spiflash.v + iverilog -s testbench -o $@ $^ `yosys-config --datdir/ice40/cells_sim.v` + +hx8kdemo_syn_tb.vvp: hx8kdemo_tb.v hx8kdemo_syn.v spiflash.v + iverilog -s testbench -o $@ $^ `yosys-config --datdir/ice40/cells_sim.v` + +hx8kdemo_syn.v: hx8kdemo.blif + yosys -p 'read_blif -wideports hx8kdemo.blif; write_verilog hx8kdemo_syn.v' + hx8kdemo.asc: hx8kdemo.pcf hx8kdemo.blif arachne-pnr -d 8k -o hx8kdemo.asc -p hx8kdemo.pcf hx8kdemo.blif @@ -49,6 +64,7 @@ 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 hx8kdemo.blif hx8kdemo.log hx8kdemo.asc hx8kdemo.rpt hx8kdemo.bin + rm -f hx8kdemo_syn.v hx8kdemo_syn_tb.vvp hx8kdemo_tb.vvp -.PHONY: testbench spiflash_tb hx8kprog clean +.PHONY: testbench spiflash_tb hx8kprog hx8ksim hx8ksynsim clean diff --git a/picosoc/firmware.s b/picosoc/firmware.s index 69fedcc..1b8900f 100644 --- a/picosoc/firmware.s +++ b/picosoc/firmware.s @@ -19,8 +19,37 @@ sw x5,64(x0) sw x5,68(x0) sw x5,72(x0) sw x5,76(x0) -li x5,0x00008067 # ret sw x5,80(x0) +sw x5,84(x0) +sw x5,88(x0) +sw x5,92(x0) +sw x5,96(x0) +sw x5,100(x0) +sw x5,104(x0) +sw x5,108(x0) +sw x5,112(x0) +sw x5,116(x0) +sw x5,120(x0) +sw x5,124(x0) +sw x5,128(x0) +sw x5,132(x0) +sw x5,136(x0) +sw x5,140(x0) +sw x5,144(x0) +sw x5,148(x0) +sw x5,152(x0) +sw x5,156(x0) +sw x5,160(x0) +sw x5,164(x0) +sw x5,168(x0) +sw x5,172(x0) +sw x5,176(x0) +sw x5,180(x0) +sw x5,184(x0) +sw x5,188(x0) +sw x5,192(x0) +li x5,0x00008067 # ret +sw x5,196(x0) # setup gpio address in x5 li x5,0x02000000 @@ -48,8 +77,8 @@ sw x6,0(x0) # calculate new entry point into RAM code slli x3,x6,2 -andi x3,x3,63 -addi x3,x3,4 +andi x3,x3,127 +addi x3,x3,32 # execute RAM code, come back to start of loop mv x1,x4 diff --git a/picosoc/hx8kdemo_tb.v b/picosoc/hx8kdemo_tb.v new file mode 100644 index 0000000..ffb292f --- /dev/null +++ b/picosoc/hx8kdemo_tb.v @@ -0,0 +1,66 @@ +/* + * 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. + * + */ + +`timescale 1 ns / 1 ps + +module testbench; + reg clk; + always #5 clk = (clk === 1'b0); + + initial begin + $dumpfile("testbench.vcd"); + $dumpvars(0, testbench); + + repeat (100000) @(posedge clk); + $finish; + end + + wire [7:0] leds; + + wire flash_csb; + wire flash_clk; + wire flash_io0; + wire flash_io1; + wire flash_io2; + wire flash_io3; + + always @(leds) begin + $display("%b", leds); + end + + hx8kdemo uut ( + .clk (clk ), + .leds (leds ), + .flash_csb(flash_csb), + .flash_clk(flash_clk), + .flash_io0(flash_io0), + .flash_io1(flash_io1), + .flash_io2(flash_io2), + .flash_io3(flash_io3) + ); + + spiflash spiflash ( + .csb(flash_csb), + .clk(flash_clk), + .io0(flash_io0), + .io1(flash_io1), + .io2(flash_io2), + .io3(flash_io3) + ); +endmodule diff --git a/picosoc/spimemio.v b/picosoc/spimemio.v index 57347cc..93227cb 100644 --- a/picosoc/spimemio.v +++ b/picosoc/spimemio.v @@ -21,10 +21,225 @@ module spimemio ( input clk, resetn, input valid, - output reg ready, + output ready, input [23:0] addr, output reg [31:0] rdata, + output flash_csb, + output flash_clk, + + 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 +); + reg xfer_resetn; + reg din_valid; + wire din_ready; + reg [7:0] din_data; + reg din_cont; + reg din_qspi; + reg din_ddr; + reg din_rd; + + wire dout_valid; + wire [7:0] dout_data; + + reg [23:0] buffer; + reg [3:0] buffer_wen; + + reg [23:0] rd_addr; + reg rd_valid; + reg rd_wait; + reg rd_inc; + + assign ready = valid && (addr == rd_addr) && rd_valid; + wire jump = valid && !ready && (addr != rd_addr+4) && rd_valid; + + spimemio_xfer xfer ( + .clk (clk ), + .resetn (xfer_resetn ), + .din_valid (din_valid ), + .din_ready (din_ready ), + .din_data (din_data ), + .din_cont (din_cont ), + .din_qspi (din_qspi ), + .din_ddr (din_ddr ), + .din_rd (din_rd ), + .dout_valid (dout_valid ), + .dout_data (dout_data ), + .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) + ); + + reg [3:0] state; + + always @(posedge clk) begin + xfer_resetn <= 1; + din_valid <= 0; + din_data <= 8'h 00; + + if (!resetn) begin + state <= 0; + xfer_resetn <= 0; + rd_valid <= 0; + din_cont <= 0; + din_qspi <= 0; + din_ddr <= 0; + din_rd <= 0; + end else begin + if (dout_valid && buffer_wen[0]) buffer[ 7: 0] <= dout_data; + if (dout_valid && buffer_wen[1]) buffer[15: 8] <= dout_data; + if (dout_valid && buffer_wen[2]) buffer[23:16] <= dout_data; + if (dout_valid && buffer_wen[3]) begin + rdata <= {dout_data, buffer}; + rd_addr <= rd_inc ? rd_addr + 4 : addr; + rd_valid <= 1; + rd_wait <= rd_inc; + rd_inc <= 1; + end + + if (dout_valid && buffer_wen) begin + buffer_wen <= 0; + end + + if (valid) + rd_wait <= 0; + + case (state) + 0: begin + din_valid <= 1; + din_data <= 8'h ff; + if (din_ready) + state <= 1; + end + 1: begin + if (dout_valid) begin + xfer_resetn <= 0; + state <= 2; + end + end + 2: begin + din_valid <= 1; + din_data <= 8'h ab; + if (din_ready) + state <= 3; + end + 3: begin + if (dout_valid) begin + xfer_resetn <= 0; + state <= 4; + end + end + 4: begin + rd_inc <= 0; + din_valid <= 1; + din_data <= 8'h 03; + if (din_ready) + state <= 5; + end + 5: begin + if (valid && !ready) begin + din_valid <= 1; + din_data <= addr[23:16]; + if (din_ready) + state <= 6; + end + end + 6: begin + din_valid <= 1; + din_data <= addr[15:8]; + if (din_ready) + state <= 7; + end + 7: begin + din_valid <= 1; + din_data <= addr[7:0]; + if (din_ready) + state <= 8; + end + 8: begin + din_valid <= 1; + din_data <= 8'h 00; + if (din_ready) begin + buffer_wen <= 4'b 0001; + state <= 9; + end + end + 9: begin + din_valid <= 1; + din_data <= 8'h 00; + if (din_ready) begin + buffer_wen <= 4'b 0010; + state <= 10; + end + end + 10: begin + din_valid <= 1; + din_data <= 8'h 00; + if (din_ready) begin + buffer_wen <= 4'b 0100; + state <= 11; + end + end + 11: begin + if (!rd_wait || valid) begin + din_valid <= 1; + din_data <= 8'h 00; + if (din_ready) begin + buffer_wen <= 4'b 1000; + state <= 8; + end + end + end + endcase + + if (jump) begin + rd_inc <= 0; + rd_valid <= 0; + xfer_resetn <= 0; + state <= 4; + end + end + end +endmodule + +module spimemio_xfer ( + input clk, resetn, + + input din_valid, + output din_ready, + input [7:0] din_data, + input din_cont, + input din_qspi, + input din_ddr, + input din_rd, + + output dout_valid, + output [7:0] dout_data, + output reg flash_csb, output reg flash_clk, @@ -43,91 +258,91 @@ module spimemio ( input flash_io2_di, input flash_io3_di ); - parameter ENABLE_PREFETCH = 1; + localparam [3:0] mode_spi = 0; + reg [3:0] mode; - reg [23:0] addr_q; - reg addr_q_vld; + reg [7:0] obuffer; + reg [7:0] ibuffer; - reg [31:0] buffer; - reg [6:0] xfer_cnt; - reg pulse_csb_8; - reg xfer_wait; - reg prefetch; + reg [3:0] count; + reg xfer_cont; + reg xfer_qspi; + reg xfer_ddr; + reg xfer_rd; + + reg [7:0] next_obuffer; + reg [7:0] next_ibuffer; + reg [3:0] next_count; + + reg fetch_next; + reg last_fetch_next; + + assign din_ready = din_valid && resetn && fetch_next; + + assign dout_valid = fetch_next && !last_fetch_next; + assign dout_data = ibuffer; + + always @* begin + flash_io0_oe = 0; + flash_io1_oe = 0; + flash_io2_oe = 0; + flash_io3_oe = 0; + + flash_io0_do = 0; + flash_io1_do = 0; + flash_io2_do = 0; + flash_io3_do = 0; + + next_obuffer = obuffer; + next_ibuffer = ibuffer; + next_count = count; + fetch_next = 0; + + case (mode) + mode_spi: begin + flash_io0_oe = 1; + flash_io0_do = obuffer[7]; + + if (flash_clk) begin + next_obuffer = {obuffer[6:0], 1'b 0}; + next_count = count - |count; + end else begin + next_ibuffer = {ibuffer[6:0], flash_io1_di}; + end + + fetch_next = (next_count == 0); + end + endcase + end always @(posedge clk) begin - ready <= 0; if (!resetn) begin - addr_q_vld <= 0; - xfer_wait <= 0; - prefetch <= 0; - - xfer_cnt <= 16; - pulse_csb_8 <= 1; - buffer <= {8'h FF, 8'h AB, 16'h 0000}; - + mode <= mode_spi; + last_fetch_next <= 1; flash_csb <= 1; - flash_clk <= 1; - - flash_io0_oe <= 0; - flash_io1_oe <= 0; - flash_io2_oe <= 0; - flash_io3_oe <= 0; - - flash_io0_do <= 0; - flash_io1_do <= 0; - flash_io2_do <= 0; - flash_io3_do <= 0; - end else - if (xfer_cnt) begin - if (xfer_cnt == 8 && pulse_csb_8) begin - pulse_csb_8 <= 0; - flash_csb <= 1; - end else - if (flash_csb) begin + flash_clk <= 0; + count <= 0; + end else begin + last_fetch_next <= fetch_next; + if (count) begin + flash_clk <= !flash_clk && !flash_csb; + obuffer <= next_obuffer; + ibuffer <= next_ibuffer; + count <= next_count; + end + if (din_valid && din_ready) begin flash_csb <= 0; - end else - if (flash_clk) begin flash_clk <= 0; - flash_io0_oe <= 1; - flash_io0_do <= buffer[31]; - end else begin - flash_clk <= 1; - buffer <= {buffer, flash_io1_di}; - 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; - if (!prefetch) - xfer_cnt <= 32; - xfer_wait <= 1; - prefetch <= 0; - end else begin - flash_csb <= 1; - buffer <= {8'h 03, addr}; - addr_q <= addr + 4; - addr_q_vld <= 1; - xfer_cnt <= 64; - xfer_wait <= 1; - prefetch <= 0; - end - end else if (ENABLE_PREFETCH && !prefetch) begin - prefetch <= 1; - xfer_cnt <= 32; - end - if (ENABLE_PREFETCH && resetn && prefetch && valid && !ready && addr_q != addr) begin - prefetch <= 0; - xfer_cnt <= 0; - xfer_wait <= 0; - flash_clk <= 1; + obuffer <= din_data; + ibuffer <= 8'h 00; + count <= 8; + + xfer_cont <= din_cont; + xfer_qspi <= din_qspi; + xfer_ddr <= din_ddr; + xfer_rd <= din_rd; + end end end endmodule