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
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
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),
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,
--- /dev/null
+#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 */
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
--- /dev/null
+#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
default_platform = "kc705"
csr_map = {
- "ddrphy": 10,
+ "spiflash": 10,
+ "ddrphy": 11,
}
csr_map.update(SDRAMSoC.csr_map)
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,