|  | /* | 
|  | *  PicoSoC - A simple example SoC using PicoRV32 | 
|  | * | 
|  | *  Copyright (C) 2017  Clifford Wolf <clifford@clifford.at> | 
|  | * | 
|  | *  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 flash_csb = 1; | 
|  | reg flash_clk = 0; | 
|  |  | 
|  | wire flash_io0; | 
|  | wire flash_io1; | 
|  | wire flash_io2; | 
|  | wire flash_io3; | 
|  |  | 
|  | reg flash_io0_oe = 0; | 
|  | reg flash_io1_oe = 0; | 
|  | reg flash_io2_oe = 0; | 
|  | reg flash_io3_oe = 0; | 
|  |  | 
|  | reg flash_io0_dout = 0; | 
|  | reg flash_io1_dout = 0; | 
|  | reg flash_io2_dout = 0; | 
|  | reg flash_io3_dout = 0; | 
|  |  | 
|  | assign flash_io0 = flash_io0_oe ? flash_io0_dout : 1'bz; | 
|  | assign flash_io1 = flash_io1_oe ? flash_io1_dout : 1'bz; | 
|  | assign flash_io2 = flash_io2_oe ? flash_io2_dout : 1'bz; | 
|  | assign flash_io3 = flash_io3_oe ? flash_io3_dout : 1'bz; | 
|  |  | 
|  | spiflash uut ( | 
|  | .csb(flash_csb), | 
|  | .clk(flash_clk), | 
|  | .io0(flash_io0), | 
|  | .io1(flash_io1), | 
|  | .io2(flash_io2), | 
|  | .io3(flash_io3) | 
|  | ); | 
|  |  | 
|  | localparam [23:0] offset = 24'h100000; | 
|  | localparam [31:0] word0 = 32'h 00000093; | 
|  | localparam [31:0] word1 = 32'h 00000193; | 
|  |  | 
|  | reg [7:0] rdata; | 
|  | integer errcount = 0; | 
|  |  | 
|  | task expect; | 
|  | input [7:0] data; | 
|  | begin | 
|  | if (data !== rdata) begin | 
|  | $display("ERROR: Got %x (%b) but expected %x (%b).", rdata, rdata, data, data); | 
|  | errcount = errcount + 1; | 
|  | end | 
|  | end | 
|  | endtask | 
|  |  | 
|  | task xfer_begin; | 
|  | begin | 
|  | #5; | 
|  | flash_csb = 0; | 
|  | $display("-- BEGIN"); | 
|  | #5; | 
|  | end | 
|  | endtask | 
|  |  | 
|  | task xfer_dummy; | 
|  | begin | 
|  | flash_io0_oe = 0; | 
|  | flash_io1_oe = 0; | 
|  | flash_io2_oe = 0; | 
|  | flash_io3_oe = 0; | 
|  |  | 
|  | #5; | 
|  | flash_clk = 1; | 
|  | #5; | 
|  | flash_clk = 0; | 
|  | #5; | 
|  | end | 
|  | endtask | 
|  |  | 
|  | task xfer_end; | 
|  | begin | 
|  | #5; | 
|  | flash_csb = 1; | 
|  | flash_io0_oe = 0; | 
|  | flash_io1_oe = 0; | 
|  | flash_io2_oe = 0; | 
|  | flash_io3_oe = 0; | 
|  | $display("-- END"); | 
|  | $display(""); | 
|  | #5; | 
|  | end | 
|  | endtask | 
|  |  | 
|  | task xfer_spi; | 
|  | input [7:0] data; | 
|  | integer i; | 
|  | begin | 
|  | flash_io0_oe = 1; | 
|  | flash_io1_oe = 0; | 
|  | flash_io2_oe = 0; | 
|  | flash_io3_oe = 0; | 
|  |  | 
|  | for (i = 0; i < 8; i=i+1) begin | 
|  | flash_io0_dout = data[7-i]; | 
|  | #5; | 
|  | flash_clk = 1; | 
|  | rdata[7-i] = flash_io1; | 
|  | #5; | 
|  | flash_clk = 0; | 
|  | end | 
|  |  | 
|  | $display("--  SPI SDR  %02x %02x", data, rdata); | 
|  | #5; | 
|  | end | 
|  | endtask | 
|  |  | 
|  | task xfer_qspi_wr; | 
|  | input [7:0] data; | 
|  | integer i; | 
|  | begin | 
|  | flash_io0_oe = 1; | 
|  | flash_io1_oe = 1; | 
|  | flash_io2_oe = 1; | 
|  | flash_io3_oe = 1; | 
|  |  | 
|  | flash_io0_dout = data[4]; | 
|  | flash_io1_dout = data[5]; | 
|  | flash_io2_dout = data[6]; | 
|  | flash_io3_dout = data[7]; | 
|  |  | 
|  | #5; | 
|  | flash_clk = 1; | 
|  |  | 
|  | #5; | 
|  | flash_clk = 0; | 
|  | flash_io0_dout = data[0]; | 
|  | flash_io1_dout = data[1]; | 
|  | flash_io2_dout = data[2]; | 
|  | flash_io3_dout = data[3]; | 
|  |  | 
|  | #5; | 
|  | flash_clk = 1; | 
|  | #5; | 
|  | flash_clk = 0; | 
|  |  | 
|  | $display("-- QSPI SDR  %02x --", data); | 
|  | #5; | 
|  | end | 
|  | endtask | 
|  |  | 
|  | task xfer_qspi_rd; | 
|  | integer i; | 
|  | begin | 
|  | flash_io0_oe = 0; | 
|  | flash_io1_oe = 0; | 
|  | flash_io2_oe = 0; | 
|  | flash_io3_oe = 0; | 
|  |  | 
|  | #5; | 
|  | flash_clk = 1; | 
|  | rdata[4] = flash_io0; | 
|  | rdata[5] = flash_io1; | 
|  | rdata[6] = flash_io2; | 
|  | rdata[7] = flash_io3; | 
|  |  | 
|  | #5; | 
|  | flash_clk = 0; | 
|  |  | 
|  | #5; | 
|  | flash_clk = 1; | 
|  | rdata[0] = flash_io0; | 
|  | rdata[1] = flash_io1; | 
|  | rdata[2] = flash_io2; | 
|  | rdata[3] = flash_io3; | 
|  |  | 
|  | #5; | 
|  | flash_clk = 0; | 
|  |  | 
|  | $display("-- QSPI SDR  -- %02x", rdata); | 
|  | #5; | 
|  | end | 
|  | endtask | 
|  |  | 
|  | task xfer_qspi_ddr_wr; | 
|  | input [7:0] data; | 
|  | integer i; | 
|  | begin | 
|  | flash_io0_oe = 1; | 
|  | flash_io1_oe = 1; | 
|  | flash_io2_oe = 1; | 
|  | flash_io3_oe = 1; | 
|  |  | 
|  | flash_io0_dout = data[4]; | 
|  | flash_io1_dout = data[5]; | 
|  | flash_io2_dout = data[6]; | 
|  | flash_io3_dout = data[7]; | 
|  |  | 
|  | #5; | 
|  | flash_clk = 1; | 
|  | flash_io0_dout = data[0]; | 
|  | flash_io1_dout = data[1]; | 
|  | flash_io2_dout = data[2]; | 
|  | flash_io3_dout = data[3]; | 
|  |  | 
|  | #5; | 
|  | flash_clk = 0; | 
|  |  | 
|  | $display("-- QSPI DDR  %02x --", data); | 
|  | #5; | 
|  | end | 
|  | endtask | 
|  |  | 
|  | task xfer_qspi_ddr_rd; | 
|  | integer i; | 
|  | begin | 
|  | flash_io0_oe = 0; | 
|  | flash_io1_oe = 0; | 
|  | flash_io2_oe = 0; | 
|  | flash_io3_oe = 0; | 
|  |  | 
|  | #5; | 
|  | flash_clk = 1; | 
|  | rdata[4] = flash_io0; | 
|  | rdata[5] = flash_io1; | 
|  | rdata[6] = flash_io2; | 
|  | rdata[7] = flash_io3; | 
|  |  | 
|  | #5; | 
|  | flash_clk = 0; | 
|  | rdata[0] = flash_io0; | 
|  | rdata[1] = flash_io1; | 
|  | rdata[2] = flash_io2; | 
|  | rdata[3] = flash_io3; | 
|  |  | 
|  | $display("-- QSPI DDR  -- %02x", rdata); | 
|  | #5; | 
|  | end | 
|  | endtask | 
|  |  | 
|  | initial begin | 
|  | $dumpfile("spiflash_tb.vcd"); | 
|  | $dumpvars(0, testbench); | 
|  | $display(""); | 
|  |  | 
|  | $display("Reset (FFh)"); | 
|  | xfer_begin; | 
|  | xfer_spi(8'h ff); | 
|  | xfer_end; | 
|  |  | 
|  | $display("Power Up (ABh)"); | 
|  | xfer_begin; | 
|  | xfer_spi(8'h ab); | 
|  | xfer_end; | 
|  |  | 
|  | $display("Read Data (03h)"); | 
|  | xfer_begin; | 
|  | xfer_spi(8'h 03); | 
|  | xfer_spi(offset[23:16]); | 
|  | xfer_spi(offset[15:8]); | 
|  | xfer_spi(offset[7:0]); | 
|  | xfer_spi(8'h 00); expect(word0[7:0]); | 
|  | xfer_spi(8'h 00); expect(word0[15:8]); | 
|  | xfer_spi(8'h 00); expect(word0[23:16]); | 
|  | xfer_spi(8'h 00); expect(word0[31:24]); | 
|  | xfer_spi(8'h 00); expect(word1[7:0]); | 
|  | xfer_spi(8'h 00); expect(word1[15:8]); | 
|  | xfer_spi(8'h 00); expect(word1[23:16]); | 
|  | xfer_spi(8'h 00); expect(word1[31:24]); | 
|  | xfer_end; | 
|  |  | 
|  | $display("Quad I/O Read (EBh)"); | 
|  | xfer_begin; | 
|  | xfer_spi(8'h eb); | 
|  | xfer_qspi_wr(offset[23:16]); | 
|  | xfer_qspi_wr(offset[15:8]); | 
|  | xfer_qspi_wr(offset[7:0]); | 
|  | xfer_qspi_wr(8'h a5); | 
|  | repeat (8) xfer_dummy; | 
|  | xfer_qspi_rd; expect(word0[7:0]); | 
|  | xfer_qspi_rd; expect(word0[15:8]); | 
|  | xfer_qspi_rd; expect(word0[23:16]); | 
|  | xfer_qspi_rd; expect(word0[31:24]); | 
|  | xfer_qspi_rd; expect(word1[7:0]); | 
|  | xfer_qspi_rd; expect(word1[15:8]); | 
|  | xfer_qspi_rd; expect(word1[23:16]); | 
|  | xfer_qspi_rd; expect(word1[31:24]); | 
|  | xfer_end; | 
|  |  | 
|  | $display("Continous Quad I/O Read"); | 
|  | xfer_begin; | 
|  | xfer_qspi_wr(offset[23:16]); | 
|  | xfer_qspi_wr(offset[15:8]); | 
|  | xfer_qspi_wr(offset[7:0]); | 
|  | xfer_qspi_wr(8'h ff); | 
|  | repeat (8) xfer_dummy; | 
|  | xfer_qspi_rd; expect(word0[7:0]); | 
|  | xfer_qspi_rd; expect(word0[15:8]); | 
|  | xfer_qspi_rd; expect(word0[23:16]); | 
|  | xfer_qspi_rd; expect(word0[31:24]); | 
|  | xfer_qspi_rd; expect(word1[7:0]); | 
|  | xfer_qspi_rd; expect(word1[15:8]); | 
|  | xfer_qspi_rd; expect(word1[23:16]); | 
|  | xfer_qspi_rd; expect(word1[31:24]); | 
|  | xfer_end; | 
|  |  | 
|  | $display("DDR Quad I/O Read (EDh)"); | 
|  | xfer_begin; | 
|  | xfer_spi(8'h ed); | 
|  | xfer_qspi_ddr_wr(offset[23:16]); | 
|  | xfer_qspi_ddr_wr(offset[15:8]); | 
|  | xfer_qspi_ddr_wr(offset[7:0]); | 
|  | xfer_qspi_ddr_wr(8'h a5); | 
|  | repeat (8) xfer_dummy; | 
|  | xfer_qspi_ddr_rd; expect(word0[7:0]); | 
|  | xfer_qspi_ddr_rd; expect(word0[15:8]); | 
|  | xfer_qspi_ddr_rd; expect(word0[23:16]); | 
|  | xfer_qspi_ddr_rd; expect(word0[31:24]); | 
|  | xfer_qspi_ddr_rd; expect(word1[7:0]); | 
|  | xfer_qspi_ddr_rd; expect(word1[15:8]); | 
|  | xfer_qspi_ddr_rd; expect(word1[23:16]); | 
|  | xfer_qspi_ddr_rd; expect(word1[31:24]); | 
|  | xfer_end; | 
|  |  | 
|  | $display("Continous DDR Quad I/O Read"); | 
|  | xfer_begin; | 
|  | xfer_qspi_ddr_wr(offset[23:16]); | 
|  | xfer_qspi_ddr_wr(offset[15:8]); | 
|  | xfer_qspi_ddr_wr(offset[7:0]); | 
|  | xfer_qspi_ddr_wr(8'h ff); | 
|  | repeat (8) xfer_dummy; | 
|  | xfer_qspi_ddr_rd; expect(word0[7:0]); | 
|  | xfer_qspi_ddr_rd; expect(word0[15:8]); | 
|  | xfer_qspi_ddr_rd; expect(word0[23:16]); | 
|  | xfer_qspi_ddr_rd; expect(word0[31:24]); | 
|  | xfer_qspi_ddr_rd; expect(word1[7:0]); | 
|  | xfer_qspi_ddr_rd; expect(word1[15:8]); | 
|  | xfer_qspi_ddr_rd; expect(word1[23:16]); | 
|  | xfer_qspi_ddr_rd; expect(word1[31:24]); | 
|  | xfer_end; | 
|  |  | 
|  | #5; | 
|  |  | 
|  | if (errcount) begin | 
|  | $display("FAIL"); | 
|  | $stop; | 
|  | end else begin | 
|  | $display("PASS"); | 
|  | end | 
|  | end | 
|  | endmodule |