diff --git a/misoclib/spiflash/__init__.py b/misoclib/spiflash/__init__.py index 8f8367316..4f8e9fd78 100644 --- a/misoclib/spiflash/__init__.py +++ b/misoclib/spiflash/__init__.py @@ -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 index 000000000..2708cdb3b --- /dev/null +++ b/software/include/base/spiflash.h @@ -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 */ diff --git a/software/libbase/Makefile b/software/libbase/Makefile index 7d19e84f6..500847522 100644 --- a/software/libbase/Makefile +++ b/software/libbase/Makefile @@ -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 index 000000000..b57b7e98f --- /dev/null +++ b/software/libbase/spiflash.c @@ -0,0 +1,123 @@ +#include + +#include + +#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 diff --git a/targets/kc705.py b/targets/kc705.py index 2bab721ec..4411601e2 100644 --- a/targets/kc705.py +++ b/targets/kc705.py @@ -64,7 +64,8 @@ class BaseSoC(SDRAMSoC): default_platform = "kc705" csr_map = { - "ddrphy": 10, + "spiflash": 10, + "ddrphy": 11, } csr_map.update(SDRAMSoC.csr_map) diff --git a/targets/ppro.py b/targets/ppro.py index 34b1a6381..761edcb88 100644 --- a/targets/ppro.py +++ b/targets/ppro.py @@ -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,