spiflash: BB write support
authorYann Sionneau <ys@m-labs.hk>
Thu, 27 Nov 2014 15:10:24 +0000 (23:10 +0800)
committerSebastien Bourdeauducq <sb@m-labs.hk>
Thu, 27 Nov 2014 15:10:39 +0000 (23:10 +0800)
misoclib/spiflash/__init__.py
software/include/base/spiflash.h [new file with mode: 0644]
software/libbase/Makefile
software/libbase/spiflash.c [new file with mode: 0644]
targets/kc705.py
targets/ppro.py

index 8f8367316632438fb4588b1c0e6d94f3e05f0356..4f8e9fd78228556d1885a21c8a684f315d451c0c 100644 (file)
@@ -3,6 +3,7 @@ from migen.bus.transactions import *
 from migen.bus import wishbone
 from migen.genlib.misc import timeline
 from migen.genlib.record import Record
+from migen.bank.description import AutoCSR, CSRStorage, CSRStatus
 
 _FAST_READ = 0x0b
 _DIOFR = 0xbb
@@ -22,27 +23,35 @@ def _format_cmd(cmd, spi_width):
                        c &= ~(1<<(b*spi_width))
        return c
 
-class SpiFlash(Module):
+class SpiFlash(Module, AutoCSR):
        def __init__(self, pads, dummy=15, div=2):
                """
-               Simple read-only SPI flash, e.g. N25Q128 on the LX9 Microboard.
+               Simple SPI flash, e.g. N25Q128 on the LX9 Microboard.
 
                Supports multi-bit pseudo-parallel reads (aka Dual or Quad I/O Fast
                Read). Only supports mode0 (cpol=0, cpha=0).
+               Supports software bitbanging (for write, erase, or other commands).
                """
                self.bus = bus = wishbone.Interface()
+               spi_width = flen(pads.dq)
+               self.bitbang = CSRStorage(4)
+               self.miso = CSRStatus()
+               self.bitbang_en = CSRStorage()
 
                ##
 
+               cs_n = Signal(reset=1)
+               clk = Signal()
+               dq_oe = Signal()
                wbone_width = flen(bus.dat_r)
-               spi_width = flen(pads.dq)
 
-               cmd_params = {
+
+               read_cmd_params = {
                        4: (_format_cmd(_QIOFR, 4), 4*8),
                        2: (_format_cmd(_DIOFR, 2), 2*8),
                        1: (_format_cmd(_FAST_READ, 1), 1*8)
                }
-               cmd, cmd_width = cmd_params[spi_width]
+               read_cmd, cmd_width = read_cmd_params[spi_width]
                addr_width = 24
 
                pads.cs_n.reset = 1
@@ -51,26 +60,43 @@ class SpiFlash(Module):
                self.specials.dq = dq.get_tristate(pads.dq)
 
                sr = Signal(max(cmd_width, addr_width, wbone_width))
+               dqs = Replicate(1, spi_width-1)
+
                self.comb += [
                        bus.dat_r.eq(sr),
-                       dq.o.eq(sr[-spi_width:]),
+                       If(self.bitbang_en.storage,
+                               pads.clk.eq(self.bitbang.storage[1]),
+                               pads.cs_n.eq(self.bitbang.storage[2]),
+                               dq.o.eq(Cat(self.bitbang.storage[0], dqs)),
+                               If(self.bitbang.storage[3],
+                                       dq.oe.eq(0)
+                               ).Else(
+                                       dq.oe.eq(1)
+                               ),
+                               If(self.bitbang.storage[1],
+                                       self.miso.status.eq(dq.i[-1])
+                               )
+                       ).Else(
+                               pads.clk.eq(clk),
+                               pads.cs_n.eq(cs_n),
+                               dq.o.eq(sr[-spi_width:]),
+                               dq.oe.eq(dq_oe)
+                       )
                ]
 
-               if div == 1:
-                       i = 0
-                       self.comb += pads.clk.eq(~ClockSignal())
-                       self.sync += sr.eq(Cat(dq.i, sr[:-spi_width]))
+               if div < 2:
+                       raise ValueError("Unsupported value \'{}\' for div parameter for SpiFlash core".format(div))
                else:
                        i = Signal(max=div)
                        dqi = Signal(spi_width)
                        self.sync += [
                                If(i == div//2 - 1,
-                                       pads.clk.eq(1),
+                                       clk.eq(1),
                                        dqi.eq(dq.i),
                                ),
                                If(i == div - 1,
                                        i.eq(0),
-                                       pads.clk.eq(0),
+                                       clk.eq(0),
                                        sr.eq(Cat(dqi, sr[:-spi_width]))
                                ).Else(
                                        i.eq(i + 1),
@@ -82,13 +108,13 @@ class SpiFlash(Module):
 
                seq = [
                        (cmd_width//spi_width*div,
-                               [dq.oe.eq(1), pads.cs_n.eq(0), sr[-cmd_width:].eq(cmd)]),
+                               [dq_oe.eq(1), cs_n.eq(0), sr[-cmd_width:].eq(read_cmd)]),
                        (addr_width//spi_width*div,
                                [sr[-addr_width:].eq(Cat(z, bus.adr))]),
                        ((dummy + wbone_width//spi_width)*div,
-                               [dq.oe.eq(0)]),
+                               [dq_oe.eq(0)]),
                        (1,
-                               [bus.ack.eq(1), pads.cs_n.eq(1)]),
+                               [bus.ack.eq(1), cs_n.eq(1)]),
                        (div, # tSHSL!
                                [bus.ack.eq(0)]),
                        (0,
diff --git a/software/include/base/spiflash.h b/software/include/base/spiflash.h
new file mode 100644 (file)
index 0000000..2708cdb
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef __SPIFLASH_H
+#define __SPIFLASH_H
+
+void write_to_flash_page(unsigned int addr, unsigned char *c, unsigned int len);
+void erase_flash_sector(unsigned int addr);
+
+#endif /* __SPIFLASH_H */
index 7d19e84f6bae8babdd1210da12be086ddbc1175b..5008475227dc65a2da119faeb7f4d4743b7956f3 100644 (file)
@@ -1,7 +1,7 @@
 MSCDIR=../..
 include $(MSCDIR)/software/common.mak
 
-OBJECTS=exception.o libc.o errno.o crc16.o crc32.o console.o system.o id.o uart.o time.o qsort.o strtod.o
+OBJECTS=exception.o libc.o errno.o crc16.o crc32.o console.o system.o id.o uart.o time.o qsort.o strtod.o spiflash.o
 
 all: crt0-$(CPU).o libbase.a libbase-nofloat.a
 
diff --git a/software/libbase/spiflash.c b/software/libbase/spiflash.c
new file mode 100644 (file)
index 0000000..b57b7e9
--- /dev/null
@@ -0,0 +1,123 @@
+#include <generated/csr.h>
+
+#include <spiflash.h>
+
+#ifdef SPIFLASH_BASE
+
+#define PAGE_PROGRAM_CMD    (0x02)
+#define WRDI_CMD        (0x04)
+#define RDSR_CMD        (0x05)
+#define WREN_CMD        (0x06)
+#define SE_CMD            (0x20)
+
+#define BITBANG_CLK        (1 << 1)
+#define BITBANG_CS_N        (1 << 2)
+#define BITBANG_DQ_INPUT    (1 << 3)
+
+#define SR_WIP            (1)
+
+#define PAGE_SIZE (256)
+#define PAGE_MASK (PAGE_SIZE - 1)
+#define SECTOR_SIZE (4096)
+#define SECTOR_MASK (SECTOR_SIZE - 1)
+
+static void flash_write_byte(unsigned char b);
+static void flash_write_addr(unsigned int addr);
+static void wait_for_device_ready(void);
+
+#define min(a,b)  (a>b?b:a)
+
+static void flash_write_byte(unsigned char b)
+{
+    int i;
+    spiflash_bitbang_write(0); // ~CS_N ~CLK
+
+    for(i = 0; i < 8; i++, b <<= 1) {
+        
+        spiflash_bitbang_write((b & 0x80) >> 7);
+        spiflash_bitbang_write(((b & 0x80) >> 7) | BITBANG_CLK);
+    }
+
+    spiflash_bitbang_write(0); // ~CS_N ~CLK
+
+}
+
+static void flash_write_addr(unsigned int addr)
+{
+    int i;
+    spiflash_bitbang_write(0);
+
+    for(i = 0; i < 24; i++, addr <<= 1) {
+        spiflash_bitbang_write((addr & 0x800000) >> 23);
+        spiflash_bitbang_write(((addr & 0x800000) >> 23) | BITBANG_CLK);
+    }
+
+    spiflash_bitbang_write(0);
+}
+
+static void wait_for_device_ready(void)
+{
+    unsigned char sr;
+    unsigned char i;
+    do {
+        sr = 0;
+        flash_write_byte(RDSR_CMD);
+        spiflash_bitbang_write(BITBANG_DQ_INPUT);
+        for(i = 0; i < 8; i++) {
+            sr <<= 1;
+            spiflash_bitbang_write(BITBANG_CLK | BITBANG_DQ_INPUT);
+            sr |= spiflash_miso_read();
+            spiflash_bitbang_write(0           | BITBANG_DQ_INPUT);
+        }
+        spiflash_bitbang_write(0);
+        spiflash_bitbang_write(BITBANG_CS_N);
+    } while(sr & SR_WIP);
+}
+
+void erase_flash_sector(unsigned int addr)
+{
+    unsigned int sector_addr = addr & ~(SECTOR_MASK);
+
+    spiflash_bitbang_en_write(1);
+
+    wait_for_device_ready();
+
+    flash_write_byte(WREN_CMD);
+    spiflash_bitbang_write(BITBANG_CS_N);
+    
+    flash_write_byte(SE_CMD);
+    flash_write_addr(sector_addr);
+    spiflash_bitbang_write(BITBANG_CS_N);
+
+    wait_for_device_ready();
+
+    spiflash_bitbang_en_write(0);
+}
+
+void write_to_flash_page(unsigned int addr, unsigned char *c, unsigned int len)
+{
+    unsigned int i;
+
+    if(len > PAGE_SIZE)
+        len = PAGE_SIZE;
+
+    spiflash_bitbang_en_write(1);
+
+    wait_for_device_ready();
+
+    flash_write_byte(WREN_CMD);
+    spiflash_bitbang_write(BITBANG_CS_N);
+    flash_write_byte(PAGE_PROGRAM_CMD);
+    flash_write_addr((unsigned int)addr);
+    for(i = 0; i < len; i++)
+        flash_write_byte(*c++);
+
+    spiflash_bitbang_write(BITBANG_CS_N);
+    spiflash_bitbang_write(0);
+
+    wait_for_device_ready();
+
+    spiflash_bitbang_en_write(0);
+}
+
+#endif
index 2bab721ecdb642cef95c5b8cb9fe60023be3fa4b..4411601e2b67af29099895ac96dd4f5c33aa88ad 100644 (file)
@@ -64,7 +64,8 @@ class BaseSoC(SDRAMSoC):
        default_platform = "kc705"
 
        csr_map = {
-               "ddrphy":       10,
+               "spiflash":     10,
+               "ddrphy":       11,
        }
        csr_map.update(SDRAMSoC.csr_map)
 
index 34b1a6381a9f337b7b3c1508aa114898a320ad75..761edcb88ebef197b82af2570b0c3885fc534fd3 100644 (file)
@@ -60,6 +60,11 @@ class _CRG(Module):
 class BaseSoC(SDRAMSoC):
        default_platform = "papilio_pro"
 
+       csr_map = {
+               "spiflash":     10,
+       }
+       csr_map.update(SDRAMSoC.csr_map)
+
        def __init__(self, platform, **kwargs):
                clk_freq = 80*1000*1000
                SDRAMSoC.__init__(self, platform, clk_freq,