From 1899c620a865841a73106dc684a72c32f0f0ebc1 Mon Sep 17 00:00:00 2001 From: BCG Date: Sat, 18 Jan 2020 07:51:21 -0500 Subject: [PATCH 01/18] Adding low level SPI flash functions --- examples/flash/main.go | 366 +++++++++++++++++++++++++++++++++++++++++ flash/device.go | 129 +++++++++++++++ flash/transport.go | 137 +++++++++++++++ 3 files changed, 632 insertions(+) create mode 100644 examples/flash/main.go create mode 100644 flash/device.go create mode 100644 flash/transport.go diff --git a/examples/flash/main.go b/examples/flash/main.go new file mode 100644 index 000000000..39e4f7251 --- /dev/null +++ b/examples/flash/main.go @@ -0,0 +1,366 @@ +package main + +import ( + "fmt" + "io" + "machine" + "os" + "strconv" + "strings" + "time" + + "tinygo.org/x/drivers/flash" +) + +const consoleBufLen = 64 +const storageBufLen = 512 + +var ( + debug = false + + input [consoleBufLen]byte + store [storageBufLen]byte + + console = machine.UART0 + readyLED = machine.LED + + tr1 *flash.Transport + dev1 *flash.Device + /* + fatdisk fs.BlockDevice + fatboot *fat.BootSectorCommon + fatfs *fat.FileSystem + rootdir fs.Directory + currdir fs.Directory + */ + //fatfsys *fat.FAT + + commands map[string]cmdfunc = map[string]cmdfunc{ + "": cmdfunc(noop), + "dbg": cmdfunc(dbg), + "lsblk": cmdfunc(lsblk), + "xxd": cmdfunc(xxd), + } +) + +type cmdfunc func(argv []string) + +const ( + StateInput = iota + StateEscape + StateEscBrc + StateCSI +) + +func main() { + + time.Sleep(3 * time.Second) + + readyLED.Configure(machine.PinConfig{Mode: machine.PinOutput}) + readyLED.High() + + tr1 = &flash.Transport{ + SPI: machine.SPI1, + MOSI: machine.SPI1_MOSI_PIN, + MISO: machine.SPI1_MISO_PIN, + SCK: machine.SPI1_SCK_PIN, + SS: machine.SPI1_CS_PIN, + } + tr1.Begin() + dev1 = &flash.Device{Transport: tr1} + + readyLED.Low() + write("SPI Configured. Reading flash info") + + dev1.Begin() + + var err error + if err != nil { + println("could not decode boot sector: " + err.Error() + "\r\n") + } + + //mnt(nil) + + prompt() + + var state = StateInput + + for i := 0; ; { + if console.Buffered() > 0 { + data, _ := console.ReadByte() + if debug { + fmt.Printf("\rdata: %x\r\n\r", data) + prompt() + console.Write(input[:i]) + } + switch state { + case StateInput: + switch data { + case 0x8: + fallthrough + case 0x7f: // this is probably wrong... works on my machine tho :) + // backspace + if i > 0 { + i -= 1 + console.Write([]byte{0x8, 0x20, 0x8}) + } + case 13: + // return key + console.Write([]byte("\r\n")) + runCommand(string(input[:i])) + prompt() + + i = 0 + continue + case 27: + // escape + state = StateEscape + default: + // anything else, just echo the character if it is printable + if strconv.IsPrint(rune(data)) { + if i < (consoleBufLen - 1) { + console.WriteByte(data) + input[i] = data + i++ + } + } + } + case StateEscape: + switch data { + case 0x5b: + state = StateEscBrc + default: + state = StateInput + } + default: + // TODO: handle escape sequences + state = StateInput + } + //time.Sleep(10 * time.Millisecond) + } + } +} + +func runCommand(line string) { + argv := strings.SplitN(strings.TrimSpace(line), " ", -1) + cmd := argv[0] + cmdfn, ok := commands[cmd] + if !ok { + println("unknown command: " + line) + return + } + cmdfn(argv) +} + +func noop(argv []string) {} + +func dbg(argv []string) { + if debug { + debug = false + println("Console debugging off") + } else { + debug = true + println("Console debugging on") + } +} + +func lsblk(argv []string) { + status, _ := dev1.ReadStatus() + serialNumber1, _ := dev1.ReadSerialNumber() + fmt.Printf( + "\n-------------------------------------\r\n"+ + " Device Information: \r\n"+ + "-------------------------------------\r\n"+ + " JEDEC ID: %v\r\n"+ + " Serial: %v\r\n"+ + " Status: %2x\r\n"+ + "-------------------------------------\r\n\r\n", + dev1.ID, + serialNumber1, + status, + ) +} + +func xxd(argv []string) { + var err error + var addr uint64 = 0x0 + var size int = 64 + switch len(argv) { + case 3: + if size, err = strconv.Atoi(argv[2]); err != nil { + println("Invalid size argument: " + err.Error() + "\r\n") + return + } + if size > storageBufLen || size < 1 { + fmt.Printf("Size of hexdump must be greater than 0 and less than %d\r\n", storageBufLen) + return + } + fallthrough + case 2: + /* + if argv[1][:2] != "0x" { + println("Invalid hex address (should start with 0x)") + return + } + */ + if addr, err = strconv.ParseUint(argv[1], 16, 32); err != nil { + println("Invalid address: " + err.Error() + "\r\n") + return + } + fallthrough + case 1: + // no args supplied, so nothing to do here, just use the defaults + default: + println("usage: xxd \r\n") + return + } + buf := store[0:size] + //fatdisk.ReadAt(buf, int64(addr)) + dev1.ReadBuffer(uint32(addr), buf) + xxdfprint(os.Stdout, uint32(addr), buf) +} + +func xxdfprint(w io.Writer, offset uint32, b []byte) { + var l int + var buf16 = make([]byte, 16) + for i, c := 0, len(b); i < c; i += 16 { + l = i + 16 + if l >= c { + l = c + } + fmt.Fprintf(w, "%08x: % x ", offset+uint32(i), b[i:l]) + for j, n := 0, l-i; j < 16; j++ { + if j >= n || !strconv.IsPrint(rune(b[i+j])) { + buf16[j] = '.' + } else { + buf16[j] = b[i+j] + } + } + console.Write(buf16) + println() + // "%s\r\n", b[i:l], "") + } +} + +func write(s string) { + println(s) +} + +func prompt() { + print("==> ") +} + +/* +const FlashBlockDeviceSectorSize = 512 + +type FlashBlockDevice struct { + flashdev *flash.Device + buf []byte + bufaddr uint32 + bufvalid bool +} + +func (fbd *FlashBlockDevice) Close() error { + // no-op + return nil +} + +func (fbd *FlashBlockDevice) Len() int64 { + // hard-coded for now + return 4096 +} + +func (fbd *FlashBlockDevice) SectorSize() int { + // hard-coded for now + return FlashBlockDeviceSectorSize +} + +func (fbd *FlashBlockDevice) ReadAt(p []byte, addr int64) (n int, err error) { + + if debug { + fmt.Printf(" -- reading %d from %08x", len(p), addr) + } + + // this is the offset from the start of the first sector that we will read + offset := addr % FlashBlockDeviceSectorSize + + // this is the address of the start of the first sector + start := uint32(addr - int64(offset)) + + // if a buffer does not already exist, create it and mark it as invalid + if fbd.buf == nil { + fbd.buf = make([]byte, FlashBlockDeviceSectorSize) + fbd.bufvalid = false + } + + // for the first sector we'll check if it is already cached or not + if !fbd.bufvalid || start != fbd.bufaddr { + if debug { + fmt.Printf(" (not cached)\r\n") + } + fbd.bufvalid = false + fbd.bufaddr = start + if err = fbd.flashdev.ReadBuffer(fbd.bufaddr, fbd.buf); err != nil { + return + } + fbd.bufvalid = true + } else if debug { + fmt.Printf(" (cached)\r\n") + } + + if debug { + fmt.Printf(" address: %08x, offset: %d, n before: %d", start, offset, n) + } + + // copy the first section of bytes into the destination buffer + n += copy(p[n:], fbd.buf[offset:]) + start += FlashBlockDeviceSectorSize + + if debug { + fmt.Printf(" - n after: %d\r\n", n) + } + + // keep looping over subsequent sectors until we've read n bytes + for c := len(p); n < c; start += FlashBlockDeviceSectorSize { + if debug { + fmt.Printf(" address: %08x, n before: %d", start, n) + } + fbd.bufvalid = false + fbd.bufaddr = start + if err = fbd.flashdev.ReadBuffer(fbd.bufaddr, fbd.buf); err != nil { + return + } + fbd.bufvalid = true + n += copy(p[n:], fbd.buf) + if debug { + fmt.Printf(" - n after: %d\r\n", n) + } + } + + return +} + +/* + if err = fbd.flashdev.ReadBuffer(uint32(off), p); err == nil { + return len(p), nil + } + +func min(a int, b int) int { + if a > b { + return b + } + return a +} + +func max(a int, b int) int { + if a < b { + return b + } + return a +} + +func (fbd *FlashBlockDevice) WriteAt(p []byte, off int64) (n int, err error) { + return 0, fmt.Errorf("Writes not yet supported") +} + +*/ diff --git a/flash/device.go b/flash/device.go new file mode 100644 index 000000000..99172631e --- /dev/null +++ b/flash/device.go @@ -0,0 +1,129 @@ +package flash + +import ( + "fmt" + "time" +) + +type JedecID [3]byte + +func (id *JedecID) Manufacturer() uint8 { + return id[0] +} + +func (id *JedecID) MemoryType() uint8 { + return id[1] +} + +func (id *JedecID) Capacity() uint8 { + return id[2] +} + +func (id *JedecID) String() string { + return fmt.Sprintf( + "%2X %2X %2X", id.Manufacturer(), id.MemoryType(), id.Capacity()) +} + +type SerialNumber uint64 + +func (sn SerialNumber) String() string { + return fmt.Sprintf("%8X", uint64(sn)) +} + +type Device struct { + Transport *Transport + ID JedecID + SerialNum SerialNumber +} + +func (dev *Device) Begin() (err error) { + + if dev.ID, err = dev.ReadJEDEC(); err != nil { + return err + } + + // TODO: should check JEDEC ID against list of known devices + + // We don't know what state the flash is in so wait for any remaining writes and then reset. + + var s byte // status + + // The write in progress bit should be low. + for s, err = dev.ReadStatus(); (s & 0x01) > 0; s, err = dev.ReadStatus() { + if err != nil { + return err + } + } + + // The suspended write/erase bit should be low. + for s, err = dev.ReadStatus2(); (s & 0x80) > 0; s, err = dev.ReadStatus() { + if err != nil { + return err + } + } + + if err = dev.Transport.RunCommand(CmdEnableReset); err != nil { + return err + } + if err = dev.Transport.RunCommand(CmdReset); err != nil { + return err + } + + // Wait 30us for the reset + stop := time.Now().UnixNano() + int64(30*time.Microsecond) + for stop > time.Now().UnixNano() { + } + + // Speed up to max device frequency + //_trans->setClockSpeed(_flash_dev->max_clock_speed_mhz*1000000UL); + + if err = dev.Transport.RunCommand(CmdWriteDisable); err != nil { + return err + } + + err = dev.WaitUntilReady() + return err +} + +func (dev *Device) ReadJEDEC() (JedecID, error) { + jedecID := make([]byte, 3) + if err := dev.Transport.ReadCommand(CmdReadJedecID, jedecID); err != nil { + return JedecID{}, err + } + return JedecID{jedecID[0], jedecID[1], jedecID[2]}, nil +} + +func (dev *Device) ReadSerialNumber() (SerialNumber, error) { + sn := make([]byte, 12) + if err := dev.Transport.ReadCommand(0x4B, sn); err != nil { + return 0, err + } + return SerialNumber(uint64(sn[11]) | uint64(sn[10])<<0x8 | + uint64(sn[9])<<0x10 | uint64(sn[8])<<0x18 | uint64(sn[7])<<0x20 | + uint64(sn[6])<<0x28 | uint64(sn[5])<<0x30 | uint64(sn[4])<<0x38), nil +} + +func (dev *Device) ReadBuffer(addr uint32, buf []byte) error { + // TODO: check if Begin() was successful + if err := dev.WaitUntilReady(); err != nil { + return err + } + return dev.Transport.ReadMemory(addr, buf) +} + +func (dev *Device) ReadStatus() (status byte, err error) { + return dev.Transport.ReadCommandByte(CmdReadStatus) +} + +func (dev *Device) ReadStatus2() (status byte, err error) { + return dev.Transport.ReadCommandByte(CmdReadStatus2) +} + +func (dev *Device) WaitUntilReady() error { + for s, err := dev.ReadStatus(); (s & 0x03) > 0; s, err = dev.ReadStatus() { + if err != nil { + return err + } + } + return nil +} diff --git a/flash/transport.go b/flash/transport.go new file mode 100644 index 000000000..40ddf77f7 --- /dev/null +++ b/flash/transport.go @@ -0,0 +1,137 @@ +package flash + +import "machine" + +type Command byte + +const ( + CmdRead Command = 0x03 // Single Read + CmdQuadRead = 0x6B // 1 line address, 4 line data + CmdReadJedecID = 0x9f + CmdPageProgram = 0x02 + CmdQuadPageProgram = 0x32 // 1 line address, 4 line data + CmdReadStatus = 0x05 + CmdReadStatus2 = 0x35 + CmdWriteStatus = 0x01 + CmdWriteStatus2 = 0x31 + CmdEnableReset = 0x66 + CmdReset = 0x99 + CmdWriteEnable = 0x06 + CmdWriteDisable = 0x04 + CmdEraseSector = 0x20 + CmdEraseBlock = 0xD8 + CmdEraseChip = 0xC7 +) + +type Transport struct { + SPI machine.SPI + MOSI machine.Pin + MISO machine.Pin + SCK machine.Pin + SS machine.Pin +} + +func (tr *Transport) Begin() { + + // Configure SPI bus + tr.SPI.Configure(machine.SPIConfig{ + Frequency: 50000000, + MISO: tr.MISO, + MOSI: tr.MOSI, + SCK: tr.SCK, + LSBFirst: false, + Mode: 0, + }) + + // Configure chip select pin + tr.SS.Configure(machine.PinConfig{Mode: machine.PinOutput}) + tr.SS.High() + +} + +func (tr *Transport) RunCommand(cmd Command) (err error) { + tr.SS.Low() + _, err = tr.SPI.Transfer(byte(cmd)) + tr.SS.High() + return +} + +func (tr *Transport) ReadCommand(cmd Command, rsp []byte) (err error) { + tr.SS.Low() + if _, err := tr.SPI.Transfer(byte(cmd)); err == nil { + err = tr.readInto(rsp) + } + tr.SS.High() + return +} + +func (tr *Transport) ReadCommandByte(cmd Command) (rsp byte, err error) { + tr.SS.Low() + if _, err := tr.SPI.Transfer(byte(cmd)); err == nil { + rsp, err = tr.SPI.Transfer(0xFF) + } + tr.SS.High() + return +} + +func (tr *Transport) WriteCommand(cmd Command, data []byte) (err error) { + tr.SS.Low() + if _, err := tr.SPI.Transfer(byte(cmd)); err == nil { + err = tr.writeFrom(data) + } + tr.SS.High() + return +} + +func (tr *Transport) EraseCommand(cmd Command, address uint32) (err error) { + tr.SS.Low() + err = tr.sendAddress(cmd, address) + tr.SS.High() + return +} + +func (tr *Transport) ReadMemory(addr uint32, rsp []byte) (err error) { + tr.SS.Low() + if err = tr.sendAddress(CmdRead, addr); err == nil { + err = tr.readInto(rsp) + } + tr.SS.High() + return +} + +func (tr *Transport) WriteMemory(addr uint32, data []byte) (err error) { + tr.SS.Low() + if err = tr.sendAddress(CmdPageProgram, addr); err == nil { + err = tr.writeFrom(data) + } + tr.SS.High() + return +} + +func (tr *Transport) sendAddress(cmd Command, addr uint32) error { + _, err := tr.SPI.Transfer(byte(cmd)) + if err == nil { + _, err = tr.SPI.Transfer(byte((addr >> 16) & 0xFF)) + } + if err == nil { + _, err = tr.SPI.Transfer(byte((addr >> 8) & 0xFF)) + } + if err == nil { + _, err = tr.SPI.Transfer(byte(addr & 0xFF)) + } + return err +} + +func (tr *Transport) readInto(rsp []byte) (err error) { + for i, c := 0, len(rsp); i < c && err == nil; i++ { + rsp[i], err = tr.SPI.Transfer(0xFF) + } + return +} + +func (tr *Transport) writeFrom(data []byte) (err error) { + for i, c := 0, len(data); i < c && err == nil; i++ { + _, err = tr.SPI.Transfer(data[i]) + } + return +} From ab33de7cd27a3e0f52052932cf0d4fddf5ed4143 Mon Sep 17 00:00:00 2001 From: BCG Date: Mon, 3 Feb 2020 02:40:54 -0500 Subject: [PATCH 02/18] Added QSPI for samd --- examples/flash/{ => qspi}/main.go | 163 ++------------------- examples/flash/spi/main.go | 232 ++++++++++++++++++++++++++++++ flash/attrs.go | 53 +++++++ flash/device.go | 50 ++++--- flash/transport.go | 135 ++++------------- flash/transport_qspi_samd.go | 174 ++++++++++++++++++++++ flash/transport_spi.go | 134 +++++++++++++++++ 7 files changed, 666 insertions(+), 275 deletions(-) rename examples/flash/{ => qspi}/main.go (56%) create mode 100644 examples/flash/spi/main.go create mode 100644 flash/attrs.go create mode 100644 flash/transport_qspi_samd.go create mode 100644 flash/transport_spi.go diff --git a/examples/flash/main.go b/examples/flash/qspi/main.go similarity index 56% rename from examples/flash/main.go rename to examples/flash/qspi/main.go index 39e4f7251..43e236de0 100644 --- a/examples/flash/main.go +++ b/examples/flash/qspi/main.go @@ -24,16 +24,14 @@ var ( console = machine.UART0 readyLED = machine.LED - tr1 *flash.Transport - dev1 *flash.Device - /* - fatdisk fs.BlockDevice - fatboot *fat.BootSectorCommon - fatfs *fat.FileSystem - rootdir fs.Directory - currdir fs.Directory - */ - //fatfsys *fat.FAT + dev = flash.NewQSPI( + machine.QSPI_CS, + machine.QSPI_SCK, + machine.QSPI_DATA0, + machine.QSPI_DATA1, + machine.QSPI_DATA2, + machine.QSPI_DATA3, + ) commands map[string]cmdfunc = map[string]cmdfunc{ "": cmdfunc(noop), @@ -59,27 +57,10 @@ func main() { readyLED.Configure(machine.PinConfig{Mode: machine.PinOutput}) readyLED.High() - tr1 = &flash.Transport{ - SPI: machine.SPI1, - MOSI: machine.SPI1_MOSI_PIN, - MISO: machine.SPI1_MISO_PIN, - SCK: machine.SPI1_SCK_PIN, - SS: machine.SPI1_CS_PIN, - } - tr1.Begin() - dev1 = &flash.Device{Transport: tr1} - readyLED.Low() - write("SPI Configured. Reading flash info") - - dev1.Begin() - - var err error - if err != nil { - println("could not decode boot sector: " + err.Error() + "\r\n") - } + write("spi Configured. Reading flash info") - //mnt(nil) + dev.Begin() prompt() @@ -165,8 +146,9 @@ func dbg(argv []string) { } func lsblk(argv []string) { - status, _ := dev1.ReadStatus() - serialNumber1, _ := dev1.ReadSerialNumber() + id, _ := dev.ReadJEDEC() + status, _ := dev.ReadStatus() + serialNumber1, _ := dev.ReadSerialNumber() fmt.Printf( "\n-------------------------------------\r\n"+ " Device Information: \r\n"+ @@ -175,7 +157,7 @@ func lsblk(argv []string) { " Serial: %v\r\n"+ " Status: %2x\r\n"+ "-------------------------------------\r\n\r\n", - dev1.ID, + id, serialNumber1, status, ) @@ -216,7 +198,7 @@ func xxd(argv []string) { } buf := store[0:size] //fatdisk.ReadAt(buf, int64(addr)) - dev1.ReadBuffer(uint32(addr), buf) + dev.ReadBuffer(uint32(addr), buf) xxdfprint(os.Stdout, uint32(addr), buf) } @@ -249,118 +231,3 @@ func write(s string) { func prompt() { print("==> ") } - -/* -const FlashBlockDeviceSectorSize = 512 - -type FlashBlockDevice struct { - flashdev *flash.Device - buf []byte - bufaddr uint32 - bufvalid bool -} - -func (fbd *FlashBlockDevice) Close() error { - // no-op - return nil -} - -func (fbd *FlashBlockDevice) Len() int64 { - // hard-coded for now - return 4096 -} - -func (fbd *FlashBlockDevice) SectorSize() int { - // hard-coded for now - return FlashBlockDeviceSectorSize -} - -func (fbd *FlashBlockDevice) ReadAt(p []byte, addr int64) (n int, err error) { - - if debug { - fmt.Printf(" -- reading %d from %08x", len(p), addr) - } - - // this is the offset from the start of the first sector that we will read - offset := addr % FlashBlockDeviceSectorSize - - // this is the address of the start of the first sector - start := uint32(addr - int64(offset)) - - // if a buffer does not already exist, create it and mark it as invalid - if fbd.buf == nil { - fbd.buf = make([]byte, FlashBlockDeviceSectorSize) - fbd.bufvalid = false - } - - // for the first sector we'll check if it is already cached or not - if !fbd.bufvalid || start != fbd.bufaddr { - if debug { - fmt.Printf(" (not cached)\r\n") - } - fbd.bufvalid = false - fbd.bufaddr = start - if err = fbd.flashdev.ReadBuffer(fbd.bufaddr, fbd.buf); err != nil { - return - } - fbd.bufvalid = true - } else if debug { - fmt.Printf(" (cached)\r\n") - } - - if debug { - fmt.Printf(" address: %08x, offset: %d, n before: %d", start, offset, n) - } - - // copy the first section of bytes into the destination buffer - n += copy(p[n:], fbd.buf[offset:]) - start += FlashBlockDeviceSectorSize - - if debug { - fmt.Printf(" - n after: %d\r\n", n) - } - - // keep looping over subsequent sectors until we've read n bytes - for c := len(p); n < c; start += FlashBlockDeviceSectorSize { - if debug { - fmt.Printf(" address: %08x, n before: %d", start, n) - } - fbd.bufvalid = false - fbd.bufaddr = start - if err = fbd.flashdev.ReadBuffer(fbd.bufaddr, fbd.buf); err != nil { - return - } - fbd.bufvalid = true - n += copy(p[n:], fbd.buf) - if debug { - fmt.Printf(" - n after: %d\r\n", n) - } - } - - return -} - -/* - if err = fbd.flashdev.ReadBuffer(uint32(off), p); err == nil { - return len(p), nil - } - -func min(a int, b int) int { - if a > b { - return b - } - return a -} - -func max(a int, b int) int { - if a < b { - return b - } - return a -} - -func (fbd *FlashBlockDevice) WriteAt(p []byte, off int64) (n int, err error) { - return 0, fmt.Errorf("Writes not yet supported") -} - -*/ diff --git a/examples/flash/spi/main.go b/examples/flash/spi/main.go new file mode 100644 index 000000000..6e1e2d200 --- /dev/null +++ b/examples/flash/spi/main.go @@ -0,0 +1,232 @@ +package main + +import ( + "fmt" + "io" + "machine" + "os" + "strconv" + "strings" + "time" + + "tinygo.org/x/drivers/flash" +) + +const consoleBufLen = 64 +const storageBufLen = 512 + +var ( + debug = false + + input [consoleBufLen]byte + store [storageBufLen]byte + + console = machine.UART0 + readyLED = machine.LED + + dev *flash.Device = flash.NewSPI( + &machine.SPI1, + machine.SPI1_MISO_PIN, + machine.SPI1_MOSI_PIN, + machine.SPI1_SCK_PIN, + machine.SPI1_CS_PIN, + ) + + commands map[string]cmdfunc = map[string]cmdfunc{ + "": cmdfunc(noop), + "dbg": cmdfunc(dbg), + "lsblk": cmdfunc(lsblk), + "xxd": cmdfunc(xxd), + } +) + +type cmdfunc func(argv []string) + +const ( + StateInput = iota + StateEscape + StateEscBrc + StateCSI +) + +func main() { + + time.Sleep(3 * time.Second) + + readyLED.Configure(machine.PinConfig{Mode: machine.PinOutput}) + readyLED.High() + + readyLED.Low() + write("spi Configured. Reading flash info") + + dev.Begin() + + prompt() + + var state = StateInput + + for i := 0; ; { + if console.Buffered() > 0 { + data, _ := console.ReadByte() + if debug { + fmt.Printf("\rdata: %x\r\n\r", data) + prompt() + console.Write(input[:i]) + } + switch state { + case StateInput: + switch data { + case 0x8: + fallthrough + case 0x7f: // this is probably wrong... works on my machine tho :) + // backspace + if i > 0 { + i -= 1 + console.Write([]byte{0x8, 0x20, 0x8}) + } + case 13: + // return key + console.Write([]byte("\r\n")) + runCommand(string(input[:i])) + prompt() + + i = 0 + continue + case 27: + // escape + state = StateEscape + default: + // anything else, just echo the character if it is printable + if strconv.IsPrint(rune(data)) { + if i < (consoleBufLen - 1) { + console.WriteByte(data) + input[i] = data + i++ + } + } + } + case StateEscape: + switch data { + case 0x5b: + state = StateEscBrc + default: + state = StateInput + } + default: + // TODO: handle escape sequences + state = StateInput + } + //time.Sleep(10 * time.Millisecond) + } + } +} + +func runCommand(line string) { + argv := strings.SplitN(strings.TrimSpace(line), " ", -1) + cmd := argv[0] + cmdfn, ok := commands[cmd] + if !ok { + println("unknown command: " + line) + return + } + cmdfn(argv) +} + +func noop(argv []string) {} + +func dbg(argv []string) { + if debug { + debug = false + println("Console debugging off") + } else { + debug = true + println("Console debugging on") + } +} + +func lsblk(argv []string) { + id, _ := dev.ReadJEDEC() + status, _ := dev.ReadStatus() + serialNumber1, _ := dev.ReadSerialNumber() + fmt.Printf( + "\n-------------------------------------\r\n"+ + " Device Information: \r\n"+ + "-------------------------------------\r\n"+ + " JEDEC ID: %v\r\n"+ + " Serial: %v\r\n"+ + " Status: %2x\r\n"+ + "-------------------------------------\r\n\r\n", + id, + serialNumber1, + status, + ) +} + +func xxd(argv []string) { + var err error + var addr uint64 = 0x0 + var size int = 64 + switch len(argv) { + case 3: + if size, err = strconv.Atoi(argv[2]); err != nil { + println("Invalid size argument: " + err.Error() + "\r\n") + return + } + if size > storageBufLen || size < 1 { + fmt.Printf("Size of hexdump must be greater than 0 and less than %d\r\n", storageBufLen) + return + } + fallthrough + case 2: + /* + if argv[1][:2] != "0x" { + println("Invalid hex address (should start with 0x)") + return + } + */ + if addr, err = strconv.ParseUint(argv[1], 16, 32); err != nil { + println("Invalid address: " + err.Error() + "\r\n") + return + } + fallthrough + case 1: + // no args supplied, so nothing to do here, just use the defaults + default: + println("usage: xxd \r\n") + return + } + buf := store[0:size] + //fatdisk.ReadAt(buf, int64(addr)) + dev.ReadBuffer(uint32(addr), buf) + xxdfprint(os.Stdout, uint32(addr), buf) +} + +func xxdfprint(w io.Writer, offset uint32, b []byte) { + var l int + var buf16 = make([]byte, 16) + for i, c := 0, len(b); i < c; i += 16 { + l = i + 16 + if l >= c { + l = c + } + fmt.Fprintf(w, "%08x: % x ", offset+uint32(i), b[i:l]) + for j, n := 0, l-i; j < 16; j++ { + if j >= n || !strconv.IsPrint(rune(b[i+j])) { + buf16[j] = '.' + } else { + buf16[j] = b[i+j] + } + } + console.Write(buf16) + println() + // "%s\r\n", b[i:l], "") + } +} + +func write(s string) { + println(s) +} + +func prompt() { + print("==> ") +} diff --git a/flash/attrs.go b/flash/attrs.go new file mode 100644 index 000000000..484ec1738 --- /dev/null +++ b/flash/attrs.go @@ -0,0 +1,53 @@ +package flash + +import "fmt" + +type JedecID struct { + ManufID uint8 + MemType uint8 + Capacity uint8 +} + +func (id *JedecID) AsUint32() uint32 { + return uint32(id.ManufID)<<16 | uint32(id.MemType)<<8 | uint32(id.Capacity) +} + +func (id JedecID) String() string { + return fmt.Sprintf("%06X", id.AsUint32()) +} + +type Attrs struct { + TotalSize uint32 + //start_up_time_us uint16 + + // Three response bytes to 0x9f JEDEC ID command. + JedecID + + // Max clock speed for all operations and the fastest read mode. + MaxClockSpeedMHz uint8 + + // Bitmask for Quad Enable bit if present. 0x00 otherwise. This is for the + // highest byte in the status register. + QuadEnableBitMask uint8 + + HasSectorProtection bool + + // Supports the 0x0b fast read command with 8 dummy cycles. + SupportsFastRead bool + + // Supports the fast read, quad output command 0x6b with 8 dummy cycles. + SupportsQSPI bool + + // Supports the quad input page program command 0x32. This is known as 1-1-4 + // because it only uses all four lines for data. + SupportsQSPIWrites bool + + // Requires a separate command 0x31 to write to the second byte of the status + // register. Otherwise two byte are written via 0x01. + WriteStatusRegisterSplit bool + + // True when the status register is a single byte. This implies the Quad + // Enable bit is in the first byte and the Read Status Register 2 command + // (0x35) is unsupported. + SingleStatusByte bool +} diff --git a/flash/device.go b/flash/device.go index 99172631e..65df169ee 100644 --- a/flash/device.go +++ b/flash/device.go @@ -5,6 +5,7 @@ import ( "time" ) +/* type JedecID [3]byte func (id *JedecID) Manufacturer() uint8 { @@ -21,8 +22,13 @@ func (id *JedecID) Capacity() uint8 { func (id *JedecID) String() string { return fmt.Sprintf( - "%2X %2X %2X", id.Manufacturer(), id.MemoryType(), id.Capacity()) + "%2X %2X %2X", + id.Manufacturer(), + id.MemoryType(), + id.Capacity(), + ) } +*/ type SerialNumber uint64 @@ -31,18 +37,21 @@ func (sn SerialNumber) String() string { } type Device struct { - Transport *Transport - ID JedecID - SerialNum SerialNumber + transport transport + attrs Attrs } func (dev *Device) Begin() (err error) { - if dev.ID, err = dev.ReadJEDEC(); err != nil { - return err - } + dev.transport.begin() // TODO: should check JEDEC ID against list of known devices + /* + if dev.ID, err = dev.ReadJEDEC(); err != nil { + return err + } + println("JEDEC:", dev.ID.String()) + */ // We don't know what state the flash is in so wait for any remaining writes and then reset. @@ -56,16 +65,16 @@ func (dev *Device) Begin() (err error) { } // The suspended write/erase bit should be low. - for s, err = dev.ReadStatus2(); (s & 0x80) > 0; s, err = dev.ReadStatus() { + for s, err = dev.ReadStatus2(); (s & 0x80) > 0; s, err = dev.ReadStatus2() { if err != nil { return err } } - if err = dev.Transport.RunCommand(CmdEnableReset); err != nil { + if err = dev.transport.runCommand(CmdEnableReset); err != nil { return err } - if err = dev.Transport.RunCommand(CmdReset); err != nil { + if err = dev.transport.runCommand(CmdReset); err != nil { return err } @@ -77,7 +86,7 @@ func (dev *Device) Begin() (err error) { // Speed up to max device frequency //_trans->setClockSpeed(_flash_dev->max_clock_speed_mhz*1000000UL); - if err = dev.Transport.RunCommand(CmdWriteDisable); err != nil { + if err = dev.transport.runCommand(CmdWriteDisable); err != nil { return err } @@ -87,7 +96,7 @@ func (dev *Device) Begin() (err error) { func (dev *Device) ReadJEDEC() (JedecID, error) { jedecID := make([]byte, 3) - if err := dev.Transport.ReadCommand(CmdReadJedecID, jedecID); err != nil { + if err := dev.transport.readCommand(CmdReadJedecID, jedecID); err != nil { return JedecID{}, err } return JedecID{jedecID[0], jedecID[1], jedecID[2]}, nil @@ -95,7 +104,7 @@ func (dev *Device) ReadJEDEC() (JedecID, error) { func (dev *Device) ReadSerialNumber() (SerialNumber, error) { sn := make([]byte, 12) - if err := dev.Transport.ReadCommand(0x4B, sn); err != nil { + if err := dev.transport.readCommand(0x4B, sn); err != nil { return 0, err } return SerialNumber(uint64(sn[11]) | uint64(sn[10])<<0x8 | @@ -108,22 +117,31 @@ func (dev *Device) ReadBuffer(addr uint32, buf []byte) error { if err := dev.WaitUntilReady(); err != nil { return err } - return dev.Transport.ReadMemory(addr, buf) + return dev.transport.readMemory(addr, buf) } func (dev *Device) ReadStatus() (status byte, err error) { - return dev.Transport.ReadCommandByte(CmdReadStatus) + buf := make([]byte, 1) + err = dev.transport.readCommand(CmdReadStatus, buf) + return buf[0], err } func (dev *Device) ReadStatus2() (status byte, err error) { - return dev.Transport.ReadCommandByte(CmdReadStatus2) + buf := make([]byte, 1) + err = dev.transport.readCommand(CmdReadStatus2, buf) + return buf[0], err } func (dev *Device) WaitUntilReady() error { + expire := time.Now().UnixNano() + int64(10*time.Second) for s, err := dev.ReadStatus(); (s & 0x03) > 0; s, err = dev.ReadStatus() { + println("wait until ready status", s) if err != nil { return err } + if time.Now().UnixNano() > expire { + return fmt.Errorf("WaitUntilReady expired") + } } return nil } diff --git a/flash/transport.go b/flash/transport.go index 40ddf77f7..c699449d3 100644 --- a/flash/transport.go +++ b/flash/transport.go @@ -1,7 +1,5 @@ package flash -import "machine" - type Command byte const ( @@ -23,115 +21,30 @@ const ( CmdEraseChip = 0xC7 ) -type Transport struct { - SPI machine.SPI - MOSI machine.Pin - MISO machine.Pin - SCK machine.Pin - SS machine.Pin -} - -func (tr *Transport) Begin() { - - // Configure SPI bus - tr.SPI.Configure(machine.SPIConfig{ - Frequency: 50000000, - MISO: tr.MISO, - MOSI: tr.MOSI, - SCK: tr.SCK, - LSBFirst: false, - Mode: 0, - }) - - // Configure chip select pin - tr.SS.Configure(machine.PinConfig{Mode: machine.PinOutput}) - tr.SS.High() - -} - -func (tr *Transport) RunCommand(cmd Command) (err error) { - tr.SS.Low() - _, err = tr.SPI.Transfer(byte(cmd)) - tr.SS.High() - return -} - -func (tr *Transport) ReadCommand(cmd Command, rsp []byte) (err error) { - tr.SS.Low() - if _, err := tr.SPI.Transfer(byte(cmd)); err == nil { - err = tr.readInto(rsp) - } - tr.SS.High() - return -} - -func (tr *Transport) ReadCommandByte(cmd Command) (rsp byte, err error) { - tr.SS.Low() - if _, err := tr.SPI.Transfer(byte(cmd)); err == nil { - rsp, err = tr.SPI.Transfer(0xFF) - } - tr.SS.High() - return -} - -func (tr *Transport) WriteCommand(cmd Command, data []byte) (err error) { - tr.SS.Low() - if _, err := tr.SPI.Transfer(byte(cmd)); err == nil { - err = tr.writeFrom(data) - } - tr.SS.High() - return -} - -func (tr *Transport) EraseCommand(cmd Command, address uint32) (err error) { - tr.SS.Low() - err = tr.sendAddress(cmd, address) - tr.SS.High() - return -} - -func (tr *Transport) ReadMemory(addr uint32, rsp []byte) (err error) { - tr.SS.Low() - if err = tr.sendAddress(CmdRead, addr); err == nil { - err = tr.readInto(rsp) - } - tr.SS.High() - return -} - -func (tr *Transport) WriteMemory(addr uint32, data []byte) (err error) { - tr.SS.Low() - if err = tr.sendAddress(CmdPageProgram, addr); err == nil { - err = tr.writeFrom(data) - } - tr.SS.High() - return -} - -func (tr *Transport) sendAddress(cmd Command, addr uint32) error { - _, err := tr.SPI.Transfer(byte(cmd)) - if err == nil { - _, err = tr.SPI.Transfer(byte((addr >> 16) & 0xFF)) - } - if err == nil { - _, err = tr.SPI.Transfer(byte((addr >> 8) & 0xFF)) - } - if err == nil { - _, err = tr.SPI.Transfer(byte(addr & 0xFF)) - } - return err -} +type Error uint8 -func (tr *Transport) readInto(rsp []byte) (err error) { - for i, c := 0, len(rsp); i < c && err == nil; i++ { - rsp[i], err = tr.SPI.Transfer(0xFF) - } - return -} +const ( + _ = iota + ErrInvalidClockSpeed Error = iota +) -func (tr *Transport) writeFrom(data []byte) (err error) { - for i, c := 0, len(data); i < c && err == nil; i++ { - _, err = tr.SPI.Transfer(data[i]) - } - return +func (err Error) Error() string { + switch err { + case ErrInvalidClockSpeed: + return "invalid clock speed" + default: + return "unspecified error" + } +} + +type transport interface { + begin() + supportQuadMode() bool + setClockSpeed(hz uint32) (err error) + runCommand(cmd Command) (err error) + readCommand(cmd Command, rsp []byte) (err error) + writeCommand(cmd Command, data []byte) (err error) + eraseCommand(cmd Command, address uint32) (err error) + readMemory(addr uint32, rsp []byte) (err error) + writeMemory(addr uint32, data []byte) (err error) } diff --git a/flash/transport_qspi_samd.go b/flash/transport_qspi_samd.go new file mode 100644 index 000000000..cf31a6501 --- /dev/null +++ b/flash/transport_qspi_samd.go @@ -0,0 +1,174 @@ +// +build atsamd51 + +package flash + +import ( + . "device/sam" + "machine" + "runtime/volatile" + "unsafe" +) + +// TODO: technically for atsamd51 we don't need to know the pins because there +// is only 1 QPSI peripheral and it is uses a fixed set of pins. However +// that might not hold true for NRF or other boards, so leaving pins in the +// signature of the contructor for now. Should investigate if this is +// necessary or not +func NewQSPI(cs, sck, d0, d1, d2, d3 machine.Pin) *Device { + return &Device{ + transport: &qspi{ + cs: cs, + sck: sck, + d0: d0, + d1: d1, + d2: d2, + d3: d3, + }, + } +} + +const ( + // QSPI address space on SAMD51 is 0x04000000 to 0x05000000 + qspi_AHB_LO = 0x04000000 + qspi_AHB_HI = 0x05000000 +) + +type qspi struct { + cs machine.Pin + sck machine.Pin + d0 machine.Pin + d1 machine.Pin + d2 machine.Pin + d3 machine.Pin +} + +func (q qspi) begin() { + + // enable main clocks + MCLK.APBCMASK.SetBits(MCLK_APBCMASK_QSPI_) + MCLK.AHBMASK.SetBits(MCLK_AHBMASK_QSPI_) + MCLK.AHBMASK.ClearBits(MCLK_AHBMASK_QSPI_2X_) + + QSPI.CTRLA.SetBits(QSPI_CTRLA_SWRST) + + // enable all pins to be PinCom + q.d0.Configure(machine.PinConfig{Mode: machine.PinCom}) + q.d1.Configure(machine.PinConfig{Mode: machine.PinCom}) + q.d2.Configure(machine.PinConfig{Mode: machine.PinCom}) + q.d3.Configure(machine.PinConfig{Mode: machine.PinCom}) + q.cs.Configure(machine.PinConfig{Mode: machine.PinCom}) + q.sck.Configure(machine.PinConfig{Mode: machine.PinCom}) + + // start out with 4Mhz + // can ignore the error, 4Mhz is always a valid speed + _ = q.setClockSpeed(4e6) + + // configure the CTRLB peripheral + QSPI.CTRLB.Reg = QSPI_CTRLB_MODE_MEMORY | + (QSPI_CTRLB_DATALEN_Msk & (QSPI_CTRLB_DATALEN_8BITS << QSPI_CTRLB_DATALEN_Pos)) | + (QSPI_CTRLB_CSMODE_Msk & (QSPI_CTRLB_CSMODE_LASTXFER << QSPI_CTRLB_CSMODE_Pos)) + + // enable the peripheral + QSPI.CTRLA.SetBits(QSPI_CTRLA_ENABLE) +} + +func (q qspi) supportQuadMode() bool { + return true +} + +func (q qspi) setClockSpeed(hz uint32) error { + if divider := machine.CPUFrequency() / hz; divider < 256 { + QSPI.BAUD.Reg = QSPI_BAUD_BAUD_Msk & (divider << QSPI_BAUD_BAUD_Pos) + } + return ErrInvalidClockSpeed +} + +func (q qspi) runCommand(cmd Command) (err error) { + const iframe = 0x0 | + QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI | + QSPI_INSTRFRAME_ADDRLEN_24BITS | + QSPI_INSTRFRAME_TFRTYPE_READ | + QSPI_INSTRFRAME_INSTREN + QSPI.INSTRCTRL.Set(uint32(cmd)) + QSPI.INSTRFRAME.Set(iframe) + QSPI.INSTRFRAME.Get() // dummy read for synchronization, as per datasheet + q.endTransfer() + return +} + +func (q qspi) readCommand(cmd Command, buf []byte) (err error) { + const iframe = 0x0 | + QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI | + QSPI_INSTRFRAME_ADDRLEN_24BITS | + QSPI_INSTRFRAME_TFRTYPE_READ | + QSPI_INSTRFRAME_INSTREN | + QSPI_INSTRFRAME_DATAEN + q.disableAndClearCache() + QSPI.INSTRCTRL.Set(uint32(cmd)) + QSPI.INSTRFRAME.Set(iframe) + QSPI.INSTRFRAME.Get() // dummy read for synchronization, as per datasheet + var ptr uintptr = qspi_AHB_LO + for i := range buf { + buf[i] = volatile.LoadUint8((*uint8)(unsafe.Pointer(ptr))) + ptr++ + } + q.endTransfer() + q.enableCache() + return +} + +func (q qspi) readMemory(addr uint32, buf []byte) (err error) { + const iframe = 0x0 | + QSPI_INSTRFRAME_WIDTH_QUAD_OUTPUT | + QSPI_INSTRFRAME_ADDRLEN_24BITS | + QSPI_INSTRFRAME_INSTREN | + QSPI_INSTRFRAME_ADDREN | + QSPI_INSTRFRAME_DATAEN | + (QSPI_INSTRFRAME_DUMMYLEN_Msk & (8 << QSPI_INSTRFRAME_DUMMYLEN_Pos)) | + (QSPI_INSTRFRAME_TFRTYPE_Msk & + (QSPI_INSTRFRAME_TFRTYPE_READMEMORY << QSPI_INSTRFRAME_TFRTYPE_Pos)) + q.disableAndClearCache() + QSPI.INSTRCTRL.Set(uint32(CmdQuadRead)) + QSPI.INSTRFRAME.Set(iframe) + QSPI.INSTRFRAME.Get() // dummy read for synchronization, as per datasheet + // TODO: need bounds check to ensure inside QSPI memory space to avoid hard fault + ln := len(buf) + sl := (*[1 << 28]byte)(unsafe.Pointer(uintptr(qspi_AHB_LO + addr)))[:ln:ln] + copy(buf, sl) + q.endTransfer() + q.enableCache() + return +} + +func (q qspi) writeCommand(cmd Command, data []byte) (err error) { + panic("implement me") +} + +func (q qspi) eraseCommand(cmd Command, address uint32) (err error) { + panic("implement me") +} + +func (q qspi) writeMemory(addr uint32, data []byte) (err error) { + panic("implement me") +} + +//go:inline +func (q qspi) enableCache() { + CMCC.CTRL.SetBits(CMCC_CTRL_CEN) +} + +//go:inline +func (q qspi) disableAndClearCache() { + CMCC.CTRL.ClearBits(CMCC_CTRL_CEN) + for CMCC.SR.HasBits(CMCC_SR_CSTS) { + } + CMCC.MAINT0.SetBits(CMCC_MAINT0_INVALL) +} + +//go:inline +func (q qspi) endTransfer() { + QSPI.CTRLA.Set(QSPI_CTRLA_ENABLE | QSPI_CTRLA_LASTXFER) + for !QSPI.INTFLAG.HasBits(QSPI_INTFLAG_INSTREND) { + } + QSPI.INTFLAG.Set(QSPI_INTFLAG_INSTREND) +} diff --git a/flash/transport_spi.go b/flash/transport_spi.go new file mode 100644 index 000000000..8acb6b8d4 --- /dev/null +++ b/flash/transport_spi.go @@ -0,0 +1,134 @@ +package flash + +import "machine" + +func NewSPI(spi *machine.SPI, miso, mosi, sck, cs machine.Pin) *Device { + return &Device{ + transport: &spiTransport{ + spi: spi, + mosi: mosi, + miso: miso, + sck: sck, + ss: cs, + }, + } +} + +type spiTransport struct { + spi *machine.SPI + mosi machine.Pin + miso machine.Pin + sck machine.Pin + ss machine.Pin +} + +func (tr *spiTransport) begin() { + // Configure spi bus + tr.setClockSpeed(5000000) + + // Configure chip select pin + tr.ss.Configure(machine.PinConfig{Mode: machine.PinOutput}) + tr.ss.High() +} + +func (tr *spiTransport) setClockSpeed(hz uint32) error { + return tr.spi.Configure(machine.SPIConfig{ + Frequency: hz, + MISO: tr.miso, + MOSI: tr.mosi, + SCK: tr.sck, + LSBFirst: false, + Mode: 0, + }) +} + +func (tr *spiTransport) supportQuadMode() bool { + return false +} + +func (tr *spiTransport) runCommand(cmd Command) (err error) { + tr.ss.Low() + _, err = tr.spi.Transfer(byte(cmd)) + tr.ss.High() + return +} + +func (tr *spiTransport) readCommand(cmd Command, rsp []byte) (err error) { + tr.ss.Low() + if _, err := tr.spi.Transfer(byte(cmd)); err == nil { + err = tr.readInto(rsp) + } + tr.ss.High() + return +} + +func (tr *spiTransport) readCommandByte(cmd Command) (rsp byte, err error) { + tr.ss.Low() + if _, err := tr.spi.Transfer(byte(cmd)); err == nil { + rsp, err = tr.spi.Transfer(0xFF) + } + tr.ss.High() + return +} + +func (tr *spiTransport) writeCommand(cmd Command, data []byte) (err error) { + tr.ss.Low() + if _, err := tr.spi.Transfer(byte(cmd)); err == nil { + err = tr.writeFrom(data) + } + tr.ss.High() + return +} + +func (tr *spiTransport) eraseCommand(cmd Command, address uint32) (err error) { + tr.ss.Low() + err = tr.sendAddress(cmd, address) + tr.ss.High() + return +} + +func (tr *spiTransport) readMemory(addr uint32, rsp []byte) (err error) { + tr.ss.Low() + if err = tr.sendAddress(CmdRead, addr); err == nil { + err = tr.readInto(rsp) + } + tr.ss.High() + return +} + +func (tr *spiTransport) writeMemory(addr uint32, data []byte) (err error) { + tr.ss.Low() + if err = tr.sendAddress(CmdPageProgram, addr); err == nil { + err = tr.writeFrom(data) + } + tr.ss.High() + return +} + +func (tr *spiTransport) sendAddress(cmd Command, addr uint32) error { + _, err := tr.spi.Transfer(byte(cmd)) + if err == nil { + _, err = tr.spi.Transfer(byte((addr >> 16) & 0xFF)) + } + if err == nil { + _, err = tr.spi.Transfer(byte((addr >> 8) & 0xFF)) + } + if err == nil { + _, err = tr.spi.Transfer(byte(addr & 0xFF)) + } + return err +} + +func (tr *spiTransport) readInto(rsp []byte) (err error) { + for i, c := 0, len(rsp); i < c && err == nil; i++ { + rsp[i], err = tr.spi.Transfer(0xFF) + } + return +} + +func (tr *spiTransport) writeFrom(data []byte) (err error) { + for i, c := 0, len(data); i < c && err == nil; i++ { + _, err = tr.spi.Transfer(data[i]) + } + return +} From b6d79e039c5dc28c1a07fe037a1ef11191bd9385 Mon Sep 17 00:00:00 2001 From: BCG Date: Wed, 5 Feb 2020 23:37:16 -0500 Subject: [PATCH 03/18] code cleanup --- flash/device.go | 25 -------------- flash/transport.go | 3 ++ flash/transport_qspi_samd.go | 63 +++++++++++++++++++----------------- flash/transport_spi.go | 3 +- 4 files changed, 39 insertions(+), 55 deletions(-) diff --git a/flash/device.go b/flash/device.go index 65df169ee..716236958 100644 --- a/flash/device.go +++ b/flash/device.go @@ -5,31 +5,6 @@ import ( "time" ) -/* -type JedecID [3]byte - -func (id *JedecID) Manufacturer() uint8 { - return id[0] -} - -func (id *JedecID) MemoryType() uint8 { - return id[1] -} - -func (id *JedecID) Capacity() uint8 { - return id[2] -} - -func (id *JedecID) String() string { - return fmt.Sprintf( - "%2X %2X %2X", - id.Manufacturer(), - id.MemoryType(), - id.Capacity(), - ) -} -*/ - type SerialNumber uint64 func (sn SerialNumber) String() string { diff --git a/flash/transport.go b/flash/transport.go index c699449d3..eb0906db0 100644 --- a/flash/transport.go +++ b/flash/transport.go @@ -26,12 +26,15 @@ type Error uint8 const ( _ = iota ErrInvalidClockSpeed Error = iota + ErrInvalidAddrRange ) func (err Error) Error() string { switch err { case ErrInvalidClockSpeed: return "invalid clock speed" + case ErrInvalidAddrRange: + return "invalid address range" default: return "unspecified error" } diff --git a/flash/transport_qspi_samd.go b/flash/transport_qspi_samd.go index cf31a6501..da756f4a7 100644 --- a/flash/transport_qspi_samd.go +++ b/flash/transport_qspi_samd.go @@ -9,11 +9,8 @@ import ( "unsafe" ) -// TODO: technically for atsamd51 we don't need to know the pins because there -// is only 1 QPSI peripheral and it is uses a fixed set of pins. However -// that might not hold true for NRF or other boards, so leaving pins in the -// signature of the contructor for now. Should investigate if this is -// necessary or not +// NewQSPI returns a pointer to a flash device that uses the QSPI peripheral to +// communicate with a serial memory chip. func NewQSPI(cs, sck, d0, d1, d2, d3 machine.Pin) *Device { return &Device{ transport: &qspi{ @@ -63,7 +60,7 @@ func (q qspi) begin() { // can ignore the error, 4Mhz is always a valid speed _ = q.setClockSpeed(4e6) - // configure the CTRLB peripheral + // configure the CTRLB register QSPI.CTRLB.Reg = QSPI_CTRLB_MODE_MEMORY | (QSPI_CTRLB_DATALEN_Msk & (QSPI_CTRLB_DATALEN_8BITS << QSPI_CTRLB_DATALEN_Pos)) | (QSPI_CTRLB_CSMODE_Msk & (QSPI_CTRLB_CSMODE_LASTXFER << QSPI_CTRLB_CSMODE_Pos)) @@ -84,27 +81,21 @@ func (q qspi) setClockSpeed(hz uint32) error { } func (q qspi) runCommand(cmd Command) (err error) { - const iframe = 0x0 | - QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI | - QSPI_INSTRFRAME_ADDRLEN_24BITS | - QSPI_INSTRFRAME_TFRTYPE_READ | - QSPI_INSTRFRAME_INSTREN QSPI.INSTRCTRL.Set(uint32(cmd)) - QSPI.INSTRFRAME.Set(iframe) + QSPI.INSTRFRAME.Set(QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI | + QSPI_INSTRFRAME_ADDRLEN_24BITS | QSPI_INSTRFRAME_INSTREN | + (QSPI_INSTRFRAME_TFRTYPE_READ << QSPI_INSTRFRAME_TFRTYPE_Pos)) QSPI.INSTRFRAME.Get() // dummy read for synchronization, as per datasheet q.endTransfer() return } func (q qspi) readCommand(cmd Command, buf []byte) (err error) { - const iframe = 0x0 | - QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI | - QSPI_INSTRFRAME_ADDRLEN_24BITS | - QSPI_INSTRFRAME_TFRTYPE_READ | - QSPI_INSTRFRAME_INSTREN | - QSPI_INSTRFRAME_DATAEN q.disableAndClearCache() QSPI.INSTRCTRL.Set(uint32(cmd)) + const iframe = QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI | QSPI_INSTRFRAME_DATAEN | + QSPI_INSTRFRAME_ADDRLEN_24BITS | QSPI_INSTRFRAME_INSTREN | + (QSPI_INSTRFRAME_TFRTYPE_READ << QSPI_INSTRFRAME_TFRTYPE_Pos) QSPI.INSTRFRAME.Set(iframe) QSPI.INSTRFRAME.Get() // dummy read for synchronization, as per datasheet var ptr uintptr = qspi_AHB_LO @@ -118,20 +109,17 @@ func (q qspi) readCommand(cmd Command, buf []byte) (err error) { } func (q qspi) readMemory(addr uint32, buf []byte) (err error) { - const iframe = 0x0 | - QSPI_INSTRFRAME_WIDTH_QUAD_OUTPUT | - QSPI_INSTRFRAME_ADDRLEN_24BITS | - QSPI_INSTRFRAME_INSTREN | - QSPI_INSTRFRAME_ADDREN | - QSPI_INSTRFRAME_DATAEN | - (QSPI_INSTRFRAME_DUMMYLEN_Msk & (8 << QSPI_INSTRFRAME_DUMMYLEN_Pos)) | - (QSPI_INSTRFRAME_TFRTYPE_Msk & - (QSPI_INSTRFRAME_TFRTYPE_READMEMORY << QSPI_INSTRFRAME_TFRTYPE_Pos)) + if (addr + uint32(len(buf))) > (qspi_AHB_HI - qspi_AHB_LO) { + return ErrInvalidAddrRange + } q.disableAndClearCache() QSPI.INSTRCTRL.Set(uint32(CmdQuadRead)) + const iframe = QSPI_INSTRFRAME_WIDTH_QUAD_OUTPUT | QSPI_INSTRFRAME_DATAEN | + QSPI_INSTRFRAME_ADDRLEN_24BITS | QSPI_INSTRFRAME_INSTREN | + QSPI_INSTRFRAME_ADDREN | (8 << QSPI_INSTRFRAME_DUMMYLEN_Pos) | + (QSPI_INSTRFRAME_TFRTYPE_READMEMORY << QSPI_INSTRFRAME_TFRTYPE_Pos) QSPI.INSTRFRAME.Set(iframe) QSPI.INSTRFRAME.Get() // dummy read for synchronization, as per datasheet - // TODO: need bounds check to ensure inside QSPI memory space to avoid hard fault ln := len(buf) sl := (*[1 << 28]byte)(unsafe.Pointer(uintptr(qspi_AHB_LO + addr)))[:ln:ln] copy(buf, sl) @@ -141,7 +129,24 @@ func (q qspi) readMemory(addr uint32, buf []byte) (err error) { } func (q qspi) writeCommand(cmd Command, data []byte) (err error) { - panic("implement me") + iframe := uint32(QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI | + QSPI_INSTRFRAME_ADDRLEN_24BITS | QSPI_INSTRFRAME_INSTREN | + (QSPI_INSTRFRAME_TFRTYPE_WRITE << QSPI_INSTRFRAME_TFRTYPE_Pos)) + if len(data) > 0 { + iframe |= QSPI_INSTRFRAME_DATAEN + } + q.disableAndClearCache() + QSPI.INSTRCTRL.Set(uint32(cmd)) + QSPI.INSTRFRAME.Set(iframe) + QSPI.INSTRFRAME.Get() // dummy read for synchronization, as per datasheet + var ptr uintptr = qspi_AHB_LO + for i := range data { + volatile.StoreUint8((*uint8)(unsafe.Pointer(ptr)), data[i]) + ptr++ + } + q.endTransfer() + q.enableCache() + return } func (q qspi) eraseCommand(cmd Command, address uint32) (err error) { diff --git a/flash/transport_spi.go b/flash/transport_spi.go index 8acb6b8d4..208bb7be0 100644 --- a/flash/transport_spi.go +++ b/flash/transport_spi.go @@ -32,7 +32,7 @@ func (tr *spiTransport) begin() { } func (tr *spiTransport) setClockSpeed(hz uint32) error { - return tr.spi.Configure(machine.SPIConfig{ + tr.spi.Configure(machine.SPIConfig{ Frequency: hz, MISO: tr.miso, MOSI: tr.mosi, @@ -40,6 +40,7 @@ func (tr *spiTransport) setClockSpeed(hz uint32) error { LSBFirst: false, Mode: 0, }) + return nil } func (tr *spiTransport) supportQuadMode() bool { From 802add9d025e841bf3d5ad471be204a818654bfa Mon Sep 17 00:00:00 2001 From: BCG Date: Wed, 12 Feb 2020 00:15:40 -0500 Subject: [PATCH 04/18] Added write operations --- flash/device.go | 67 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/flash/device.go b/flash/device.go index 716236958..db04f37c6 100644 --- a/flash/device.go +++ b/flash/device.go @@ -5,6 +5,12 @@ import ( "time" ) +const ( + SFLASH_BLOCK_SIZE = 64 * 1024 + SFLASH_SECTOR_SIZE = 4 * 1024 + SFLASH_PAGE_SIZE = 256 +) + type SerialNumber uint64 func (sn SerialNumber) String() string { @@ -95,6 +101,65 @@ func (dev *Device) ReadBuffer(addr uint32, buf []byte) error { return dev.transport.readMemory(addr, buf) } +func (dev *Device) WriteBuffer(addr uint32, buf []byte) (n int, err error) { + remain := uint32(len(buf)) + buffer := uint32(0) + for remain > 0 { + if err = dev.WaitUntilReady(); err != nil { + return + } + if err = dev.WriteEnable(); err != nil { + return + } + leftOnPage := SFLASH_PAGE_SIZE - (addr & (SFLASH_PAGE_SIZE - 1)) + toWrite := remain + if leftOnPage < remain { + toWrite = leftOnPage + } + if err = dev.transport.writeMemory(addr, buf[buffer:buffer+toWrite]); err != nil { + return + } + remain -= toWrite + buffer += toWrite + addr += toWrite + } + return len(buf) - int(remain), nil +} + +func (dev *Device) WriteEnable() error { + return dev.transport.runCommand(CmdWriteEnable) +} + +func (dev *Device) EraseBlock(addr uint32) error { + if err := dev.WaitUntilReady(); err != nil { + return err + } + if err := dev.WriteEnable(); err != nil { + return err + } + return dev.transport.eraseCommand(CmdEraseBlock, addr*SFLASH_BLOCK_SIZE) +} + +func (dev *Device) EraseSector(addr uint32) error { + if err := dev.WaitUntilReady(); err != nil { + return err + } + if err := dev.WriteEnable(); err != nil { + return err + } + return dev.transport.eraseCommand(CmdEraseSector, addr*SFLASH_SECTOR_SIZE) +} + +func (dev *Device) EraseChip() error { + if err := dev.WaitUntilReady(); err != nil { + return err + } + if err := dev.WriteEnable(); err != nil { + return err + } + return dev.transport.runCommand(CmdEraseChip) +} + func (dev *Device) ReadStatus() (status byte, err error) { buf := make([]byte, 1) err = dev.transport.readCommand(CmdReadStatus, buf) @@ -108,7 +173,7 @@ func (dev *Device) ReadStatus2() (status byte, err error) { } func (dev *Device) WaitUntilReady() error { - expire := time.Now().UnixNano() + int64(10*time.Second) + expire := time.Now().UnixNano() + int64(1*time.Second) for s, err := dev.ReadStatus(); (s & 0x03) > 0; s, err = dev.ReadStatus() { println("wait until ready status", s) if err != nil { From 10ee5a3038d13791f23670a40296ef33c57eb9fb Mon Sep 17 00:00:00 2001 From: BCG Date: Wed, 12 Feb 2020 01:01:07 -0500 Subject: [PATCH 05/18] Reorganized and removed dot imports --- flash/attrs.go | 53 ------- flash/device.go | 187 ----------------------- flash/flash.go | 289 +++++++++++++++++++++++++++++++++++ flash/transport.go | 53 ------- flash/transport_qspi_samd.go | 92 +++++------ flash/transport_spi.go | 18 +-- 6 files changed, 344 insertions(+), 348 deletions(-) delete mode 100644 flash/attrs.go delete mode 100644 flash/device.go create mode 100644 flash/flash.go delete mode 100644 flash/transport.go diff --git a/flash/attrs.go b/flash/attrs.go deleted file mode 100644 index 484ec1738..000000000 --- a/flash/attrs.go +++ /dev/null @@ -1,53 +0,0 @@ -package flash - -import "fmt" - -type JedecID struct { - ManufID uint8 - MemType uint8 - Capacity uint8 -} - -func (id *JedecID) AsUint32() uint32 { - return uint32(id.ManufID)<<16 | uint32(id.MemType)<<8 | uint32(id.Capacity) -} - -func (id JedecID) String() string { - return fmt.Sprintf("%06X", id.AsUint32()) -} - -type Attrs struct { - TotalSize uint32 - //start_up_time_us uint16 - - // Three response bytes to 0x9f JEDEC ID command. - JedecID - - // Max clock speed for all operations and the fastest read mode. - MaxClockSpeedMHz uint8 - - // Bitmask for Quad Enable bit if present. 0x00 otherwise. This is for the - // highest byte in the status register. - QuadEnableBitMask uint8 - - HasSectorProtection bool - - // Supports the 0x0b fast read command with 8 dummy cycles. - SupportsFastRead bool - - // Supports the fast read, quad output command 0x6b with 8 dummy cycles. - SupportsQSPI bool - - // Supports the quad input page program command 0x32. This is known as 1-1-4 - // because it only uses all four lines for data. - SupportsQSPIWrites bool - - // Requires a separate command 0x31 to write to the second byte of the status - // register. Otherwise two byte are written via 0x01. - WriteStatusRegisterSplit bool - - // True when the status register is a single byte. This implies the Quad - // Enable bit is in the first byte and the Read Status Register 2 command - // (0x35) is unsupported. - SingleStatusByte bool -} diff --git a/flash/device.go b/flash/device.go deleted file mode 100644 index db04f37c6..000000000 --- a/flash/device.go +++ /dev/null @@ -1,187 +0,0 @@ -package flash - -import ( - "fmt" - "time" -) - -const ( - SFLASH_BLOCK_SIZE = 64 * 1024 - SFLASH_SECTOR_SIZE = 4 * 1024 - SFLASH_PAGE_SIZE = 256 -) - -type SerialNumber uint64 - -func (sn SerialNumber) String() string { - return fmt.Sprintf("%8X", uint64(sn)) -} - -type Device struct { - transport transport - attrs Attrs -} - -func (dev *Device) Begin() (err error) { - - dev.transport.begin() - - // TODO: should check JEDEC ID against list of known devices - /* - if dev.ID, err = dev.ReadJEDEC(); err != nil { - return err - } - println("JEDEC:", dev.ID.String()) - */ - - // We don't know what state the flash is in so wait for any remaining writes and then reset. - - var s byte // status - - // The write in progress bit should be low. - for s, err = dev.ReadStatus(); (s & 0x01) > 0; s, err = dev.ReadStatus() { - if err != nil { - return err - } - } - - // The suspended write/erase bit should be low. - for s, err = dev.ReadStatus2(); (s & 0x80) > 0; s, err = dev.ReadStatus2() { - if err != nil { - return err - } - } - - if err = dev.transport.runCommand(CmdEnableReset); err != nil { - return err - } - if err = dev.transport.runCommand(CmdReset); err != nil { - return err - } - - // Wait 30us for the reset - stop := time.Now().UnixNano() + int64(30*time.Microsecond) - for stop > time.Now().UnixNano() { - } - - // Speed up to max device frequency - //_trans->setClockSpeed(_flash_dev->max_clock_speed_mhz*1000000UL); - - if err = dev.transport.runCommand(CmdWriteDisable); err != nil { - return err - } - - err = dev.WaitUntilReady() - return err -} - -func (dev *Device) ReadJEDEC() (JedecID, error) { - jedecID := make([]byte, 3) - if err := dev.transport.readCommand(CmdReadJedecID, jedecID); err != nil { - return JedecID{}, err - } - return JedecID{jedecID[0], jedecID[1], jedecID[2]}, nil -} - -func (dev *Device) ReadSerialNumber() (SerialNumber, error) { - sn := make([]byte, 12) - if err := dev.transport.readCommand(0x4B, sn); err != nil { - return 0, err - } - return SerialNumber(uint64(sn[11]) | uint64(sn[10])<<0x8 | - uint64(sn[9])<<0x10 | uint64(sn[8])<<0x18 | uint64(sn[7])<<0x20 | - uint64(sn[6])<<0x28 | uint64(sn[5])<<0x30 | uint64(sn[4])<<0x38), nil -} - -func (dev *Device) ReadBuffer(addr uint32, buf []byte) error { - // TODO: check if Begin() was successful - if err := dev.WaitUntilReady(); err != nil { - return err - } - return dev.transport.readMemory(addr, buf) -} - -func (dev *Device) WriteBuffer(addr uint32, buf []byte) (n int, err error) { - remain := uint32(len(buf)) - buffer := uint32(0) - for remain > 0 { - if err = dev.WaitUntilReady(); err != nil { - return - } - if err = dev.WriteEnable(); err != nil { - return - } - leftOnPage := SFLASH_PAGE_SIZE - (addr & (SFLASH_PAGE_SIZE - 1)) - toWrite := remain - if leftOnPage < remain { - toWrite = leftOnPage - } - if err = dev.transport.writeMemory(addr, buf[buffer:buffer+toWrite]); err != nil { - return - } - remain -= toWrite - buffer += toWrite - addr += toWrite - } - return len(buf) - int(remain), nil -} - -func (dev *Device) WriteEnable() error { - return dev.transport.runCommand(CmdWriteEnable) -} - -func (dev *Device) EraseBlock(addr uint32) error { - if err := dev.WaitUntilReady(); err != nil { - return err - } - if err := dev.WriteEnable(); err != nil { - return err - } - return dev.transport.eraseCommand(CmdEraseBlock, addr*SFLASH_BLOCK_SIZE) -} - -func (dev *Device) EraseSector(addr uint32) error { - if err := dev.WaitUntilReady(); err != nil { - return err - } - if err := dev.WriteEnable(); err != nil { - return err - } - return dev.transport.eraseCommand(CmdEraseSector, addr*SFLASH_SECTOR_SIZE) -} - -func (dev *Device) EraseChip() error { - if err := dev.WaitUntilReady(); err != nil { - return err - } - if err := dev.WriteEnable(); err != nil { - return err - } - return dev.transport.runCommand(CmdEraseChip) -} - -func (dev *Device) ReadStatus() (status byte, err error) { - buf := make([]byte, 1) - err = dev.transport.readCommand(CmdReadStatus, buf) - return buf[0], err -} - -func (dev *Device) ReadStatus2() (status byte, err error) { - buf := make([]byte, 1) - err = dev.transport.readCommand(CmdReadStatus2, buf) - return buf[0], err -} - -func (dev *Device) WaitUntilReady() error { - expire := time.Now().UnixNano() + int64(1*time.Second) - for s, err := dev.ReadStatus(); (s & 0x03) > 0; s, err = dev.ReadStatus() { - println("wait until ready status", s) - if err != nil { - return err - } - if time.Now().UnixNano() > expire { - return fmt.Errorf("WaitUntilReady expired") - } - } - return nil -} diff --git a/flash/flash.go b/flash/flash.go new file mode 100644 index 000000000..282a6e3be --- /dev/null +++ b/flash/flash.go @@ -0,0 +1,289 @@ +package flash + +import ( + "fmt" + "time" +) + +const ( + cmdRead = 0x03 // Single Read + cmdQuadRead = 0x6B // 1 line address, 4 line data + cmdReadJedecID = 0x9f + cmdPageProgram = 0x02 + cmdQuadPageProgram = 0x32 // 1 line address, 4 line data + cmdReadStatus = 0x05 + cmdReadStatus2 = 0x35 + cmdWriteStatus = 0x01 + cmdWriteStatus2 = 0x31 + cmdEnableReset = 0x66 + cmdReset = 0x99 + cmdWriteEnable = 0x06 + cmdWriteDisable = 0x04 + cmdEraseSector = 0x20 + cmdEraseBlock = 0xD8 + cmdEraseChip = 0xC7 +) + +const ( + BlockSize = 64 * 1024 + SectorSize = 4 * 1024 + PageSize = 256 +) + +type Error uint8 + +const ( + _ = iota + ErrInvalidClockSpeed Error = iota + ErrInvalidAddrRange +) + +func (err Error) Error() string { + switch err { + case ErrInvalidClockSpeed: + return "invalid clock speed" + case ErrInvalidAddrRange: + return "invalid address range" + default: + return "unspecified error" + } +} + +type JedecID struct { + ManufID uint8 + MemType uint8 + Capacity uint8 +} + +func (id *JedecID) Uint32() uint32 { + return uint32(id.ManufID)<<16 | uint32(id.MemType)<<8 | uint32(id.Capacity) +} + +func (id *JedecID) String() string { + return fmt.Sprintf("%06X", id.Uint32()) +} + +type SerialNumber uint64 + +func (sn SerialNumber) String() string { + return fmt.Sprintf("%8X", uint64(sn)) +} + +type Device struct { + transport transport + attrs Attrs +} + +type transport interface { + begin() + supportQuadMode() bool + setClockSpeed(hz uint32) (err error) + runCommand(cmd byte) (err error) + readCommand(cmd byte, rsp []byte) (err error) + writeCommand(cmd byte, data []byte) (err error) + eraseCommand(cmd byte, address uint32) (err error) + readMemory(addr uint32, rsp []byte) (err error) + writeMemory(addr uint32, data []byte) (err error) +} + +type Attrs struct { + TotalSize uint32 + //start_up_time_us uint16 + + // Three response bytes to 0x9f JEDEC ID command. + JedecID + + // Max clock speed for all operations and the fastest read mode. + MaxClockSpeedMHz uint8 + + // Bitmask for Quad Enable bit if present. 0x00 otherwise. This is for the + // highest byte in the status register. + QuadEnableBitMask uint8 + + HasSectorProtection bool + + // Supports the 0x0b fast read command with 8 dummy cycles. + SupportsFastRead bool + + // Supports the fast read, quad output command 0x6b with 8 dummy cycles. + SupportsQSPI bool + + // Supports the quad input page program command 0x32. This is known as 1-1-4 + // because it only uses all four lines for data. + SupportsQSPIWrites bool + + // Requires a separate command 0x31 to write to the second byte of the status + // register. Otherwise two byte are written via 0x01. + WriteStatusRegisterSplit bool + + // True when the status register is a single byte. This implies the Quad + // Enable bit is in the first byte and the Read Status Register 2 command + // (0x35) is unsupported. + SingleStatusByte bool +} + +func (dev *Device) Begin() (err error) { + + dev.transport.begin() + + // TODO: should check JEDEC ID against list of known devices + /* + if dev.ID, err = dev.ReadJEDEC(); err != nil { + return err + } + println("JEDEC:", dev.ID.String()) + */ + + // We don't know what state the flash is in so wait for any remaining writes and then reset. + + var s byte // status + + // The write in progress bit should be low. + for s, err = dev.ReadStatus(); (s & 0x01) > 0; s, err = dev.ReadStatus() { + if err != nil { + return err + } + } + + // The suspended write/erase bit should be low. + for s, err = dev.ReadStatus2(); (s & 0x80) > 0; s, err = dev.ReadStatus2() { + if err != nil { + return err + } + } + + if err = dev.transport.runCommand(cmdEnableReset); err != nil { + return err + } + if err = dev.transport.runCommand(cmdReset); err != nil { + return err + } + + // Wait 30us for the reset + stop := time.Now().UnixNano() + int64(30*time.Microsecond) + for stop > time.Now().UnixNano() { + } + + // Speed up to max device frequency + //_trans->setClockSpeed(_flash_dev->max_clock_speed_mhz*1000000UL); + + if err = dev.transport.runCommand(cmdWriteDisable); err != nil { + return err + } + + err = dev.WaitUntilReady() + return err +} + +func (dev *Device) ReadJEDEC() (JedecID, error) { + jedecID := make([]byte, 3) + if err := dev.transport.readCommand(cmdReadJedecID, jedecID); err != nil { + return JedecID{}, err + } + return JedecID{jedecID[0], jedecID[1], jedecID[2]}, nil +} + +func (dev *Device) ReadSerialNumber() (SerialNumber, error) { + sn := make([]byte, 12) + if err := dev.transport.readCommand(0x4B, sn); err != nil { + return 0, err + } + return SerialNumber(uint64(sn[11]) | uint64(sn[10])<<0x8 | + uint64(sn[9])<<0x10 | uint64(sn[8])<<0x18 | uint64(sn[7])<<0x20 | + uint64(sn[6])<<0x28 | uint64(sn[5])<<0x30 | uint64(sn[4])<<0x38), nil +} + +func (dev *Device) ReadBuffer(addr uint32, buf []byte) error { + // TODO: check if Begin() was successful + if err := dev.WaitUntilReady(); err != nil { + return err + } + return dev.transport.readMemory(addr, buf) +} + +func (dev *Device) WriteBuffer(addr uint32, buf []byte) (n int, err error) { + remain := uint32(len(buf)) + idx := uint32(0) + for remain > 0 { + if err = dev.WaitUntilReady(); err != nil { + return + } + if err = dev.WriteEnable(); err != nil { + return + } + leftOnPage := PageSize - (addr & (PageSize - 1)) + toWrite := remain + if leftOnPage < remain { + toWrite = leftOnPage + } + if err = dev.transport.writeMemory(addr, buf[idx:idx+toWrite]); err != nil { + return + } + idx += toWrite + addr += toWrite + remain -= toWrite + } + return len(buf) - int(remain), nil +} + +func (dev *Device) WriteEnable() error { + return dev.transport.runCommand(cmdWriteEnable) +} + +func (dev *Device) EraseBlock(addr uint32) error { + if err := dev.WaitUntilReady(); err != nil { + return err + } + if err := dev.WriteEnable(); err != nil { + return err + } + return dev.transport.eraseCommand(cmdEraseBlock, addr*BlockSize) +} + +func (dev *Device) EraseSector(addr uint32) error { + if err := dev.WaitUntilReady(); err != nil { + return err + } + if err := dev.WriteEnable(); err != nil { + return err + } + return dev.transport.eraseCommand(cmdEraseSector, addr*SectorSize) +} + +func (dev *Device) EraseChip() error { + if err := dev.WaitUntilReady(); err != nil { + return err + } + if err := dev.WriteEnable(); err != nil { + return err + } + return dev.transport.runCommand(cmdEraseChip) +} + +// ReadStatus reads the value from status register 1 of the device +func (dev *Device) ReadStatus() (status byte, err error) { + buf := make([]byte, 1) + err = dev.transport.readCommand(cmdReadStatus, buf) + return buf[0], err +} + +// ReadStatus2 reads the value from status register 2 of the device +func (dev *Device) ReadStatus2() (status byte, err error) { + buf := make([]byte, 1) + err = dev.transport.readCommand(cmdReadStatus2, buf) + return buf[0], err +} + +func (dev *Device) WaitUntilReady() error { + expire := time.Now().UnixNano() + int64(1*time.Second) + for s, err := dev.ReadStatus(); (s & 0x03) > 0; s, err = dev.ReadStatus() { + println("wait until ready status", s) + if err != nil { + return err + } + if time.Now().UnixNano() > expire { + return fmt.Errorf("WaitUntilReady expired") + } + } + return nil +} diff --git a/flash/transport.go b/flash/transport.go deleted file mode 100644 index eb0906db0..000000000 --- a/flash/transport.go +++ /dev/null @@ -1,53 +0,0 @@ -package flash - -type Command byte - -const ( - CmdRead Command = 0x03 // Single Read - CmdQuadRead = 0x6B // 1 line address, 4 line data - CmdReadJedecID = 0x9f - CmdPageProgram = 0x02 - CmdQuadPageProgram = 0x32 // 1 line address, 4 line data - CmdReadStatus = 0x05 - CmdReadStatus2 = 0x35 - CmdWriteStatus = 0x01 - CmdWriteStatus2 = 0x31 - CmdEnableReset = 0x66 - CmdReset = 0x99 - CmdWriteEnable = 0x06 - CmdWriteDisable = 0x04 - CmdEraseSector = 0x20 - CmdEraseBlock = 0xD8 - CmdEraseChip = 0xC7 -) - -type Error uint8 - -const ( - _ = iota - ErrInvalidClockSpeed Error = iota - ErrInvalidAddrRange -) - -func (err Error) Error() string { - switch err { - case ErrInvalidClockSpeed: - return "invalid clock speed" - case ErrInvalidAddrRange: - return "invalid address range" - default: - return "unspecified error" - } -} - -type transport interface { - begin() - supportQuadMode() bool - setClockSpeed(hz uint32) (err error) - runCommand(cmd Command) (err error) - readCommand(cmd Command, rsp []byte) (err error) - writeCommand(cmd Command, data []byte) (err error) - eraseCommand(cmd Command, address uint32) (err error) - readMemory(addr uint32, rsp []byte) (err error) - writeMemory(addr uint32, data []byte) (err error) -} diff --git a/flash/transport_qspi_samd.go b/flash/transport_qspi_samd.go index da756f4a7..ff6963ede 100644 --- a/flash/transport_qspi_samd.go +++ b/flash/transport_qspi_samd.go @@ -3,7 +3,7 @@ package flash import ( - . "device/sam" + "device/sam" "machine" "runtime/volatile" "unsafe" @@ -42,11 +42,11 @@ type qspi struct { func (q qspi) begin() { // enable main clocks - MCLK.APBCMASK.SetBits(MCLK_APBCMASK_QSPI_) - MCLK.AHBMASK.SetBits(MCLK_AHBMASK_QSPI_) - MCLK.AHBMASK.ClearBits(MCLK_AHBMASK_QSPI_2X_) + sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_QSPI_) + sam.MCLK.AHBMASK.SetBits(sam.MCLK_AHBMASK_QSPI_) + sam.MCLK.AHBMASK.ClearBits(sam.MCLK_AHBMASK_QSPI_2X_) - QSPI.CTRLA.SetBits(QSPI_CTRLA_SWRST) + sam.QSPI.CTRLA.SetBits(sam.QSPI_CTRLA_SWRST) // enable all pins to be PinCom q.d0.Configure(machine.PinConfig{Mode: machine.PinCom}) @@ -61,12 +61,12 @@ func (q qspi) begin() { _ = q.setClockSpeed(4e6) // configure the CTRLB register - QSPI.CTRLB.Reg = QSPI_CTRLB_MODE_MEMORY | - (QSPI_CTRLB_DATALEN_Msk & (QSPI_CTRLB_DATALEN_8BITS << QSPI_CTRLB_DATALEN_Pos)) | - (QSPI_CTRLB_CSMODE_Msk & (QSPI_CTRLB_CSMODE_LASTXFER << QSPI_CTRLB_CSMODE_Pos)) + sam.QSPI.CTRLB.Reg = sam.QSPI_CTRLB_MODE_MEMORY | + (sam.QSPI_CTRLB_DATALEN_8BITS << sam.QSPI_CTRLB_DATALEN_Pos) | + (sam.QSPI_CTRLB_CSMODE_LASTXFER << sam.QSPI_CTRLB_CSMODE_Pos) // enable the peripheral - QSPI.CTRLA.SetBits(QSPI_CTRLA_ENABLE) + sam.QSPI.CTRLA.SetBits(sam.QSPI_CTRLA_ENABLE) } func (q qspi) supportQuadMode() bool { @@ -75,29 +75,29 @@ func (q qspi) supportQuadMode() bool { func (q qspi) setClockSpeed(hz uint32) error { if divider := machine.CPUFrequency() / hz; divider < 256 { - QSPI.BAUD.Reg = QSPI_BAUD_BAUD_Msk & (divider << QSPI_BAUD_BAUD_Pos) + sam.QSPI.BAUD.Reg = sam.QSPI_BAUD_BAUD_Msk & (divider << sam.QSPI_BAUD_BAUD_Pos) } return ErrInvalidClockSpeed } -func (q qspi) runCommand(cmd Command) (err error) { - QSPI.INSTRCTRL.Set(uint32(cmd)) - QSPI.INSTRFRAME.Set(QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI | - QSPI_INSTRFRAME_ADDRLEN_24BITS | QSPI_INSTRFRAME_INSTREN | - (QSPI_INSTRFRAME_TFRTYPE_READ << QSPI_INSTRFRAME_TFRTYPE_Pos)) - QSPI.INSTRFRAME.Get() // dummy read for synchronization, as per datasheet +func (q qspi) runCommand(cmd byte) (err error) { + sam.QSPI.INSTRCTRL.Set(uint32(cmd)) + sam.QSPI.INSTRFRAME.Set(sam.QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI | + sam.QSPI_INSTRFRAME_ADDRLEN_24BITS | sam.QSPI_INSTRFRAME_INSTREN | + (sam.QSPI_INSTRFRAME_TFRTYPE_READ << sam.QSPI_INSTRFRAME_TFRTYPE_Pos)) + sam.QSPI.INSTRFRAME.Get() // dummy read for synchronization, as per datasheet q.endTransfer() return } -func (q qspi) readCommand(cmd Command, buf []byte) (err error) { +func (q qspi) readCommand(cmd byte, buf []byte) (err error) { q.disableAndClearCache() - QSPI.INSTRCTRL.Set(uint32(cmd)) - const iframe = QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI | QSPI_INSTRFRAME_DATAEN | - QSPI_INSTRFRAME_ADDRLEN_24BITS | QSPI_INSTRFRAME_INSTREN | - (QSPI_INSTRFRAME_TFRTYPE_READ << QSPI_INSTRFRAME_TFRTYPE_Pos) - QSPI.INSTRFRAME.Set(iframe) - QSPI.INSTRFRAME.Get() // dummy read for synchronization, as per datasheet + sam.QSPI.INSTRCTRL.Set(uint32(cmd)) + const iframe = sam.QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI | sam.QSPI_INSTRFRAME_DATAEN | + sam.QSPI_INSTRFRAME_ADDRLEN_24BITS | sam.QSPI_INSTRFRAME_INSTREN | + (sam.QSPI_INSTRFRAME_TFRTYPE_READ << sam.QSPI_INSTRFRAME_TFRTYPE_Pos) + sam.QSPI.INSTRFRAME.Set(iframe) + sam.QSPI.INSTRFRAME.Get() // dummy read for synchronization, as per datasheet var ptr uintptr = qspi_AHB_LO for i := range buf { buf[i] = volatile.LoadUint8((*uint8)(unsafe.Pointer(ptr))) @@ -113,13 +113,13 @@ func (q qspi) readMemory(addr uint32, buf []byte) (err error) { return ErrInvalidAddrRange } q.disableAndClearCache() - QSPI.INSTRCTRL.Set(uint32(CmdQuadRead)) - const iframe = QSPI_INSTRFRAME_WIDTH_QUAD_OUTPUT | QSPI_INSTRFRAME_DATAEN | - QSPI_INSTRFRAME_ADDRLEN_24BITS | QSPI_INSTRFRAME_INSTREN | - QSPI_INSTRFRAME_ADDREN | (8 << QSPI_INSTRFRAME_DUMMYLEN_Pos) | - (QSPI_INSTRFRAME_TFRTYPE_READMEMORY << QSPI_INSTRFRAME_TFRTYPE_Pos) - QSPI.INSTRFRAME.Set(iframe) - QSPI.INSTRFRAME.Get() // dummy read for synchronization, as per datasheet + sam.QSPI.INSTRCTRL.Set(uint32(cmdQuadRead)) + const iframe = sam.QSPI_INSTRFRAME_WIDTH_QUAD_OUTPUT | sam.QSPI_INSTRFRAME_DATAEN | + sam.QSPI_INSTRFRAME_ADDRLEN_24BITS | sam.QSPI_INSTRFRAME_INSTREN | + sam.QSPI_INSTRFRAME_ADDREN | (8 << sam.QSPI_INSTRFRAME_DUMMYLEN_Pos) | + (sam.QSPI_INSTRFRAME_TFRTYPE_READMEMORY << sam.QSPI_INSTRFRAME_TFRTYPE_Pos) + sam.QSPI.INSTRFRAME.Set(iframe) + sam.QSPI.INSTRFRAME.Get() // dummy read for synchronization, as per datasheet ln := len(buf) sl := (*[1 << 28]byte)(unsafe.Pointer(uintptr(qspi_AHB_LO + addr)))[:ln:ln] copy(buf, sl) @@ -128,17 +128,17 @@ func (q qspi) readMemory(addr uint32, buf []byte) (err error) { return } -func (q qspi) writeCommand(cmd Command, data []byte) (err error) { - iframe := uint32(QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI | - QSPI_INSTRFRAME_ADDRLEN_24BITS | QSPI_INSTRFRAME_INSTREN | - (QSPI_INSTRFRAME_TFRTYPE_WRITE << QSPI_INSTRFRAME_TFRTYPE_Pos)) +func (q qspi) writeCommand(cmd byte, data []byte) (err error) { + iframe := uint32(sam.QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI | + sam.QSPI_INSTRFRAME_ADDRLEN_24BITS | sam.QSPI_INSTRFRAME_INSTREN | + (sam.QSPI_INSTRFRAME_TFRTYPE_WRITE << sam.QSPI_INSTRFRAME_TFRTYPE_Pos)) if len(data) > 0 { - iframe |= QSPI_INSTRFRAME_DATAEN + iframe |= sam.QSPI_INSTRFRAME_DATAEN } q.disableAndClearCache() - QSPI.INSTRCTRL.Set(uint32(cmd)) - QSPI.INSTRFRAME.Set(iframe) - QSPI.INSTRFRAME.Get() // dummy read for synchronization, as per datasheet + sam.QSPI.INSTRCTRL.Set(uint32(cmd)) + sam.QSPI.INSTRFRAME.Set(iframe) + sam.QSPI.INSTRFRAME.Get() // dummy read for synchronization, as per datasheet var ptr uintptr = qspi_AHB_LO for i := range data { volatile.StoreUint8((*uint8)(unsafe.Pointer(ptr)), data[i]) @@ -149,7 +149,7 @@ func (q qspi) writeCommand(cmd Command, data []byte) (err error) { return } -func (q qspi) eraseCommand(cmd Command, address uint32) (err error) { +func (q qspi) eraseCommand(cmd byte, address uint32) (err error) { panic("implement me") } @@ -159,21 +159,21 @@ func (q qspi) writeMemory(addr uint32, data []byte) (err error) { //go:inline func (q qspi) enableCache() { - CMCC.CTRL.SetBits(CMCC_CTRL_CEN) + sam.CMCC.CTRL.SetBits(sam.CMCC_CTRL_CEN) } //go:inline func (q qspi) disableAndClearCache() { - CMCC.CTRL.ClearBits(CMCC_CTRL_CEN) - for CMCC.SR.HasBits(CMCC_SR_CSTS) { + sam.CMCC.CTRL.ClearBits(sam.CMCC_CTRL_CEN) + for sam.CMCC.SR.HasBits(sam.CMCC_SR_CSTS) { } - CMCC.MAINT0.SetBits(CMCC_MAINT0_INVALL) + sam.CMCC.MAINT0.SetBits(sam.CMCC_MAINT0_INVALL) } //go:inline func (q qspi) endTransfer() { - QSPI.CTRLA.Set(QSPI_CTRLA_ENABLE | QSPI_CTRLA_LASTXFER) - for !QSPI.INTFLAG.HasBits(QSPI_INTFLAG_INSTREND) { + sam.QSPI.CTRLA.Set(sam.QSPI_CTRLA_ENABLE | sam.QSPI_CTRLA_LASTXFER) + for !sam.QSPI.INTFLAG.HasBits(sam.QSPI_INTFLAG_INSTREND) { } - QSPI.INTFLAG.Set(QSPI_INTFLAG_INSTREND) + sam.QSPI.INTFLAG.Set(sam.QSPI_INTFLAG_INSTREND) } diff --git a/flash/transport_spi.go b/flash/transport_spi.go index 208bb7be0..87c0a16f2 100644 --- a/flash/transport_spi.go +++ b/flash/transport_spi.go @@ -2,7 +2,7 @@ package flash import "machine" -func NewSPI(spi *machine.SPI, miso, mosi, sck, cs machine.Pin) *Device { +func NewSPI(spi *machine.SPI, mosi, miso, sck, cs machine.Pin) *Device { return &Device{ transport: &spiTransport{ spi: spi, @@ -47,14 +47,14 @@ func (tr *spiTransport) supportQuadMode() bool { return false } -func (tr *spiTransport) runCommand(cmd Command) (err error) { +func (tr *spiTransport) runCommand(cmd byte) (err error) { tr.ss.Low() _, err = tr.spi.Transfer(byte(cmd)) tr.ss.High() return } -func (tr *spiTransport) readCommand(cmd Command, rsp []byte) (err error) { +func (tr *spiTransport) readCommand(cmd byte, rsp []byte) (err error) { tr.ss.Low() if _, err := tr.spi.Transfer(byte(cmd)); err == nil { err = tr.readInto(rsp) @@ -63,7 +63,7 @@ func (tr *spiTransport) readCommand(cmd Command, rsp []byte) (err error) { return } -func (tr *spiTransport) readCommandByte(cmd Command) (rsp byte, err error) { +func (tr *spiTransport) readCommandByte(cmd byte) (rsp byte, err error) { tr.ss.Low() if _, err := tr.spi.Transfer(byte(cmd)); err == nil { rsp, err = tr.spi.Transfer(0xFF) @@ -72,7 +72,7 @@ func (tr *spiTransport) readCommandByte(cmd Command) (rsp byte, err error) { return } -func (tr *spiTransport) writeCommand(cmd Command, data []byte) (err error) { +func (tr *spiTransport) writeCommand(cmd byte, data []byte) (err error) { tr.ss.Low() if _, err := tr.spi.Transfer(byte(cmd)); err == nil { err = tr.writeFrom(data) @@ -81,7 +81,7 @@ func (tr *spiTransport) writeCommand(cmd Command, data []byte) (err error) { return } -func (tr *spiTransport) eraseCommand(cmd Command, address uint32) (err error) { +func (tr *spiTransport) eraseCommand(cmd byte, address uint32) (err error) { tr.ss.Low() err = tr.sendAddress(cmd, address) tr.ss.High() @@ -90,7 +90,7 @@ func (tr *spiTransport) eraseCommand(cmd Command, address uint32) (err error) { func (tr *spiTransport) readMemory(addr uint32, rsp []byte) (err error) { tr.ss.Low() - if err = tr.sendAddress(CmdRead, addr); err == nil { + if err = tr.sendAddress(cmdRead, addr); err == nil { err = tr.readInto(rsp) } tr.ss.High() @@ -99,14 +99,14 @@ func (tr *spiTransport) readMemory(addr uint32, rsp []byte) (err error) { func (tr *spiTransport) writeMemory(addr uint32, data []byte) (err error) { tr.ss.Low() - if err = tr.sendAddress(CmdPageProgram, addr); err == nil { + if err = tr.sendAddress(cmdPageProgram, addr); err == nil { err = tr.writeFrom(data) } tr.ss.High() return } -func (tr *spiTransport) sendAddress(cmd Command, addr uint32) error { +func (tr *spiTransport) sendAddress(cmd byte, addr uint32) error { _, err := tr.spi.Transfer(byte(cmd)) if err == nil { _, err = tr.spi.Transfer(byte((addr >> 16) & 0xFF)) From 64267f34925e5cece2bb1615dd853ca86243c841 Mon Sep 17 00:00:00 2001 From: BCG Date: Wed, 12 Feb 2020 01:09:24 -0500 Subject: [PATCH 06/18] adding comments --- flash/flash.go | 36 ++++++++++++++++++++---------------- flash/transport_qspi_samd.go | 31 ++++++++++++++----------------- 2 files changed, 34 insertions(+), 33 deletions(-) diff --git a/flash/flash.go b/flash/flash.go index 282a6e3be..166ad02bf 100644 --- a/flash/flash.go +++ b/flash/flash.go @@ -8,26 +8,31 @@ import ( const ( cmdRead = 0x03 // Single Read cmdQuadRead = 0x6B // 1 line address, 4 line data - cmdReadJedecID = 0x9f - cmdPageProgram = 0x02 + cmdReadJedecID = 0x9f // read the JEDEC ID from the device + cmdPageProgram = 0x02 // program a page of memory using single-bit tx cmdQuadPageProgram = 0x32 // 1 line address, 4 line data - cmdReadStatus = 0x05 - cmdReadStatus2 = 0x35 - cmdWriteStatus = 0x01 - cmdWriteStatus2 = 0x31 - cmdEnableReset = 0x66 - cmdReset = 0x99 - cmdWriteEnable = 0x06 - cmdWriteDisable = 0x04 - cmdEraseSector = 0x20 - cmdEraseBlock = 0xD8 - cmdEraseChip = 0xC7 + cmdReadStatus = 0x05 // read status register 1 + cmdReadStatus2 = 0x35 // read status register 2 + cmdWriteStatus = 0x01 // write status register 1 + cmdWriteStatus2 = 0x31 // write status register 2 + cmdEnableReset = 0x66 // enable reset + cmdReset = 0x99 // perform reset + cmdWriteEnable = 0x06 // write-enable memory + cmdWriteDisable = 0x04 // write-protect memory + cmdEraseSector = 0x20 // erase a sector of memory + cmdEraseBlock = 0xD8 // erase a block of memory + cmdEraseChip = 0xC7 // erase the entire chip ) const ( - BlockSize = 64 * 1024 + // BlockSize is the number of bytes in a block for most/all NOR flash memory + BlockSize = 64 * 1024 + + // SectorSize is the number of bytes in a sector for most/all NOR flash memory SectorSize = 4 * 1024 - PageSize = 256 + + // PageSize is the number of bytes in a page for most/all NOR flash memory + PageSize = 256 ) type Error uint8 @@ -277,7 +282,6 @@ func (dev *Device) ReadStatus2() (status byte, err error) { func (dev *Device) WaitUntilReady() error { expire := time.Now().UnixNano() + int64(1*time.Second) for s, err := dev.ReadStatus(); (s & 0x03) > 0; s, err = dev.ReadStatus() { - println("wait until ready status", s) if err != nil { return err } diff --git a/flash/transport_qspi_samd.go b/flash/transport_qspi_samd.go index ff6963ede..2376fd048 100644 --- a/flash/transport_qspi_samd.go +++ b/flash/transport_qspi_samd.go @@ -13,7 +13,7 @@ import ( // communicate with a serial memory chip. func NewQSPI(cs, sck, d0, d1, d2, d3 machine.Pin) *Device { return &Device{ - transport: &qspi{ + transport: &qspiTransport{ cs: cs, sck: sck, d0: d0, @@ -30,7 +30,7 @@ const ( qspi_AHB_HI = 0x05000000 ) -type qspi struct { +type qspiTransport struct { cs machine.Pin sck machine.Pin d0 machine.Pin @@ -39,7 +39,7 @@ type qspi struct { d3 machine.Pin } -func (q qspi) begin() { +func (q qspiTransport) begin() { // enable main clocks sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_QSPI_) @@ -69,18 +69,18 @@ func (q qspi) begin() { sam.QSPI.CTRLA.SetBits(sam.QSPI_CTRLA_ENABLE) } -func (q qspi) supportQuadMode() bool { +func (q qspiTransport) supportQuadMode() bool { return true } -func (q qspi) setClockSpeed(hz uint32) error { +func (q qspiTransport) setClockSpeed(hz uint32) error { if divider := machine.CPUFrequency() / hz; divider < 256 { sam.QSPI.BAUD.Reg = sam.QSPI_BAUD_BAUD_Msk & (divider << sam.QSPI_BAUD_BAUD_Pos) } return ErrInvalidClockSpeed } -func (q qspi) runCommand(cmd byte) (err error) { +func (q qspiTransport) runCommand(cmd byte) (err error) { sam.QSPI.INSTRCTRL.Set(uint32(cmd)) sam.QSPI.INSTRFRAME.Set(sam.QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI | sam.QSPI_INSTRFRAME_ADDRLEN_24BITS | sam.QSPI_INSTRFRAME_INSTREN | @@ -90,7 +90,7 @@ func (q qspi) runCommand(cmd byte) (err error) { return } -func (q qspi) readCommand(cmd byte, buf []byte) (err error) { +func (q qspiTransport) readCommand(cmd byte, buf []byte) (err error) { q.disableAndClearCache() sam.QSPI.INSTRCTRL.Set(uint32(cmd)) const iframe = sam.QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI | sam.QSPI_INSTRFRAME_DATAEN | @@ -108,7 +108,7 @@ func (q qspi) readCommand(cmd byte, buf []byte) (err error) { return } -func (q qspi) readMemory(addr uint32, buf []byte) (err error) { +func (q qspiTransport) readMemory(addr uint32, buf []byte) (err error) { if (addr + uint32(len(buf))) > (qspi_AHB_HI - qspi_AHB_LO) { return ErrInvalidAddrRange } @@ -128,7 +128,7 @@ func (q qspi) readMemory(addr uint32, buf []byte) (err error) { return } -func (q qspi) writeCommand(cmd byte, data []byte) (err error) { +func (q qspiTransport) writeCommand(cmd byte, data []byte) (err error) { iframe := uint32(sam.QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI | sam.QSPI_INSTRFRAME_ADDRLEN_24BITS | sam.QSPI_INSTRFRAME_INSTREN | (sam.QSPI_INSTRFRAME_TFRTYPE_WRITE << sam.QSPI_INSTRFRAME_TFRTYPE_Pos)) @@ -149,29 +149,26 @@ func (q qspi) writeCommand(cmd byte, data []byte) (err error) { return } -func (q qspi) eraseCommand(cmd byte, address uint32) (err error) { +func (q qspiTransport) eraseCommand(cmd byte, address uint32) (err error) { panic("implement me") } -func (q qspi) writeMemory(addr uint32, data []byte) (err error) { +func (q qspiTransport) writeMemory(addr uint32, data []byte) (err error) { panic("implement me") } -//go:inline -func (q qspi) enableCache() { +func (q qspiTransport) enableCache() { sam.CMCC.CTRL.SetBits(sam.CMCC_CTRL_CEN) } -//go:inline -func (q qspi) disableAndClearCache() { +func (q qspiTransport) disableAndClearCache() { sam.CMCC.CTRL.ClearBits(sam.CMCC_CTRL_CEN) for sam.CMCC.SR.HasBits(sam.CMCC_SR_CSTS) { } sam.CMCC.MAINT0.SetBits(sam.CMCC_MAINT0_INVALL) } -//go:inline -func (q qspi) endTransfer() { +func (q qspiTransport) endTransfer() { sam.QSPI.CTRLA.Set(sam.QSPI_CTRLA_ENABLE | sam.QSPI_CTRLA_LASTXFER) for !sam.QSPI.INTFLAG.HasBits(sam.QSPI_INTFLAG_INSTREND) { } From 0f9e1c70e344c1c461d9b2b4b1a21b76e7979aa3 Mon Sep 17 00:00:00 2001 From: BCG Date: Wed, 12 Feb 2020 09:06:26 -0500 Subject: [PATCH 07/18] Adding more comments --- flash/flash.go | 31 +++++++++++-------------------- flash/transport_qspi_samd.go | 5 ++++- flash/transport_spi.go | 14 ++++++++++++++ 3 files changed, 29 insertions(+), 21 deletions(-) diff --git a/flash/flash.go b/flash/flash.go index 166ad02bf..7cdaf877a 100644 --- a/flash/flash.go +++ b/flash/flash.go @@ -6,11 +6,11 @@ import ( ) const ( - cmdRead = 0x03 // Single Read - cmdQuadRead = 0x6B // 1 line address, 4 line data - cmdReadJedecID = 0x9f // read the JEDEC ID from the device - cmdPageProgram = 0x02 // program a page of memory using single-bit tx - cmdQuadPageProgram = 0x32 // 1 line address, 4 line data + cmdRead = 0x03 // read memory using single-bit transfer + cmdQuadRead = 0x6B // read with 1 line address, 4 line data + cmdReadJedecID = 0x9F // read the JEDEC ID from the device + cmdPageProgram = 0x02 // write a page of memory using single-bit transfer + cmdQuadPageProgram = 0x32 // write with 1 line address, 4 line data cmdReadStatus = 0x05 // read status register 1 cmdReadStatus2 = 0x35 // read status register 2 cmdWriteStatus = 0x01 // write status register 1 @@ -46,11 +46,11 @@ const ( func (err Error) Error() string { switch err { case ErrInvalidClockSpeed: - return "invalid clock speed" + return "flash: invalid clock speed" case ErrInvalidAddrRange: - return "invalid address range" + return "flash: invalid address range" default: - return "unspecified error" + return "flash: unspecified error" } } @@ -79,18 +79,6 @@ type Device struct { attrs Attrs } -type transport interface { - begin() - supportQuadMode() bool - setClockSpeed(hz uint32) (err error) - runCommand(cmd byte) (err error) - readCommand(cmd byte, rsp []byte) (err error) - writeCommand(cmd byte, data []byte) (err error) - eraseCommand(cmd byte, address uint32) (err error) - readMemory(addr uint32, rsp []byte) (err error) - writeMemory(addr uint32, data []byte) (err error) -} - type Attrs struct { TotalSize uint32 //start_up_time_us uint16 @@ -235,6 +223,7 @@ func (dev *Device) WriteEnable() error { return dev.transport.runCommand(cmdWriteEnable) } +// EraseBlock erases a block of memory at the specified address func (dev *Device) EraseBlock(addr uint32) error { if err := dev.WaitUntilReady(); err != nil { return err @@ -245,6 +234,7 @@ func (dev *Device) EraseBlock(addr uint32) error { return dev.transport.eraseCommand(cmdEraseBlock, addr*BlockSize) } +// EraseSector erases a sector of memory at the specified address func (dev *Device) EraseSector(addr uint32) error { if err := dev.WaitUntilReady(); err != nil { return err @@ -255,6 +245,7 @@ func (dev *Device) EraseSector(addr uint32) error { return dev.transport.eraseCommand(cmdEraseSector, addr*SectorSize) } +// EraseChip erases the entire flash memory chip func (dev *Device) EraseChip() error { if err := dev.WaitUntilReady(); err != nil { return err diff --git a/flash/transport_qspi_samd.go b/flash/transport_qspi_samd.go index 2376fd048..f94c2f761 100644 --- a/flash/transport_qspi_samd.go +++ b/flash/transport_qspi_samd.go @@ -24,9 +24,12 @@ func NewQSPI(cs, sck, d0, d1, d2, d3 machine.Pin) *Device { } } +// QSPI address space on SAMD51 is 0x04000000 to 0x05000000 const ( - // QSPI address space on SAMD51 is 0x04000000 to 0x05000000 + // Low address of the QSPI address space on SAMD51 qspi_AHB_LO = 0x04000000 + + // High address of the QSPI address space on SAMD51 qspi_AHB_HI = 0x05000000 ) diff --git a/flash/transport_spi.go b/flash/transport_spi.go index 87c0a16f2..4ce109d8c 100644 --- a/flash/transport_spi.go +++ b/flash/transport_spi.go @@ -2,6 +2,20 @@ package flash import "machine" +type transport interface { + begin() + supportQuadMode() bool + setClockSpeed(hz uint32) (err error) + runCommand(cmd byte) (err error) + readCommand(cmd byte, rsp []byte) (err error) + writeCommand(cmd byte, data []byte) (err error) + eraseCommand(cmd byte, address uint32) (err error) + readMemory(addr uint32, rsp []byte) (err error) + writeMemory(addr uint32, data []byte) (err error) +} + +// NewSPI returns a pointer to a flash device that uses a SPI peripheral to +// communicate with a serial memory chip. func NewSPI(spi *machine.SPI, mosi, miso, sck, cs machine.Pin) *Device { return &Device{ transport: &spiTransport{ From 0a5ca81441daaa48deaa08c4bb1cc23b92416d12 Mon Sep 17 00:00:00 2001 From: BCG Date: Fri, 14 Feb 2020 00:06:50 -0500 Subject: [PATCH 08/18] Added descriptors for known chips and consolidated SPI/QSPI examples --- .../main.go => console/console_example.go} | 84 ++-- examples/flash/console/qspi/main.go | 21 + examples/flash/console/spi/main.go | 20 + examples/flash/spi/main.go | 232 ---------- flash/devices.go | 432 ++++++++++++++++++ flash/flash.go | 34 +- flash/transport_qspi_samd.go | 2 +- flash/transport_spi.go | 4 +- 8 files changed, 546 insertions(+), 283 deletions(-) rename examples/flash/{qspi/main.go => console/console_example.go} (74%) create mode 100644 examples/flash/console/qspi/main.go create mode 100644 examples/flash/console/spi/main.go delete mode 100644 examples/flash/spi/main.go create mode 100644 flash/devices.go diff --git a/examples/flash/qspi/main.go b/examples/flash/console/console_example.go similarity index 74% rename from examples/flash/qspi/main.go rename to examples/flash/console/console_example.go index 43e236de0..fd4e874c2 100644 --- a/examples/flash/qspi/main.go +++ b/examples/flash/console/console_example.go @@ -1,4 +1,4 @@ -package main +package console_example import ( "fmt" @@ -7,7 +7,6 @@ import ( "os" "strconv" "strings" - "time" "tinygo.org/x/drivers/flash" ) @@ -21,22 +20,16 @@ var ( input [consoleBufLen]byte store [storageBufLen]byte - console = machine.UART0 - readyLED = machine.LED + console = machine.UART0 - dev = flash.NewQSPI( - machine.QSPI_CS, - machine.QSPI_SCK, - machine.QSPI_DATA0, - machine.QSPI_DATA1, - machine.QSPI_DATA2, - machine.QSPI_DATA3, - ) + dev *flash.Device commands map[string]cmdfunc = map[string]cmdfunc{ "": cmdfunc(noop), "dbg": cmdfunc(dbg), + "erase": cmdfunc(erase), "lsblk": cmdfunc(lsblk), + "write": cmdfunc(write), "xxd": cmdfunc(xxd), } ) @@ -50,17 +43,12 @@ const ( StateCSI ) -func main() { - - time.Sleep(3 * time.Second) - - readyLED.Configure(machine.PinConfig{Mode: machine.PinOutput}) - readyLED.High() - - readyLED.Low() - write("spi Configured. Reading flash info") +func RunFor(device *flash.Device) { - dev.Begin() + dev = device + dev.Configure(&flash.DeviceConfig{ + Identifier: flash.DefaultDeviceIdentifier, + }) prompt() @@ -117,7 +105,6 @@ func main() { // TODO: handle escape sequences state = StateInput } - //time.Sleep(10 * time.Millisecond) } } } @@ -163,6 +150,45 @@ func lsblk(argv []string) { ) } +func erase(argv []string) { + if len(argv) < 3 { + println("usage: erase ") + } + var err error + var addr uint64 = 0x0 + if addr, err = strconv.ParseUint(argv[2], 16, 32); err != nil { + println("Invalid address: " + err.Error() + "\r\n") + return + } + if argv[1] == "block" { + if err = dev.EraseBlock(uint32(addr)); err != nil { + println("Block erase error: " + err.Error() + "\r\n") + } + } else if argv[1] == "sector" { + if err = dev.EraseSector(uint32(addr)); err != nil { + println("Block erase error: " + err.Error() + "\r\n") + } + } else { + println("usage: erase ") + } +} + +func write(argv []string) { + if len(argv) < 3 { + println("usage: write ") + } + var err error + var addr uint64 = 0x0 + if addr, err = strconv.ParseUint(argv[1], 16, 32); err != nil { + println("Invalid address: " + err.Error() + "\r\n") + return + } + buf := []byte(argv[2]) + if _, err = dev.WriteBuffer(uint32(addr), buf); err != nil { + println("Write error: " + err.Error() + "\r\n") + } +} + func xxd(argv []string) { var err error var addr uint64 = 0x0 @@ -179,12 +205,6 @@ func xxd(argv []string) { } fallthrough case 2: - /* - if argv[1][:2] != "0x" { - println("Invalid hex address (should start with 0x)") - return - } - */ if addr, err = strconv.ParseUint(argv[1], 16, 32); err != nil { println("Invalid address: " + err.Error() + "\r\n") return @@ -197,7 +217,6 @@ func xxd(argv []string) { return } buf := store[0:size] - //fatdisk.ReadAt(buf, int64(addr)) dev.ReadBuffer(uint32(addr), buf) xxdfprint(os.Stdout, uint32(addr), buf) } @@ -220,14 +239,9 @@ func xxdfprint(w io.Writer, offset uint32, b []byte) { } console.Write(buf16) println() - // "%s\r\n", b[i:l], "") } } -func write(s string) { - println(s) -} - func prompt() { print("==> ") } diff --git a/examples/flash/console/qspi/main.go b/examples/flash/console/qspi/main.go new file mode 100644 index 000000000..6a71931e3 --- /dev/null +++ b/examples/flash/console/qspi/main.go @@ -0,0 +1,21 @@ +package main + +import ( + "machine" + + "tinygo.org/x/drivers/examples/flash/console" + "tinygo.org/x/drivers/flash" +) + +func main() { + console_example.RunFor( + flash.NewQSPI( + machine.QSPI_CS, + machine.QSPI_SCK, + machine.QSPI_DATA0, + machine.QSPI_DATA1, + machine.QSPI_DATA2, + machine.QSPI_DATA3, + ), + ) +} diff --git a/examples/flash/console/spi/main.go b/examples/flash/console/spi/main.go new file mode 100644 index 000000000..b2e0bb1cd --- /dev/null +++ b/examples/flash/console/spi/main.go @@ -0,0 +1,20 @@ +package main + +import ( + "machine" + + "tinygo.org/x/drivers/examples/flash/console" + "tinygo.org/x/drivers/flash" +) + +func main() { + console_example.RunFor( + flash.NewSPI( + &machine.SPI1, + machine.SPI1_MOSI_PIN, + machine.SPI1_MISO_PIN, + machine.SPI1_SCK_PIN, + machine.SPI1_CS_PIN, + ), + ) +} diff --git a/examples/flash/spi/main.go b/examples/flash/spi/main.go deleted file mode 100644 index 6e1e2d200..000000000 --- a/examples/flash/spi/main.go +++ /dev/null @@ -1,232 +0,0 @@ -package main - -import ( - "fmt" - "io" - "machine" - "os" - "strconv" - "strings" - "time" - - "tinygo.org/x/drivers/flash" -) - -const consoleBufLen = 64 -const storageBufLen = 512 - -var ( - debug = false - - input [consoleBufLen]byte - store [storageBufLen]byte - - console = machine.UART0 - readyLED = machine.LED - - dev *flash.Device = flash.NewSPI( - &machine.SPI1, - machine.SPI1_MISO_PIN, - machine.SPI1_MOSI_PIN, - machine.SPI1_SCK_PIN, - machine.SPI1_CS_PIN, - ) - - commands map[string]cmdfunc = map[string]cmdfunc{ - "": cmdfunc(noop), - "dbg": cmdfunc(dbg), - "lsblk": cmdfunc(lsblk), - "xxd": cmdfunc(xxd), - } -) - -type cmdfunc func(argv []string) - -const ( - StateInput = iota - StateEscape - StateEscBrc - StateCSI -) - -func main() { - - time.Sleep(3 * time.Second) - - readyLED.Configure(machine.PinConfig{Mode: machine.PinOutput}) - readyLED.High() - - readyLED.Low() - write("spi Configured. Reading flash info") - - dev.Begin() - - prompt() - - var state = StateInput - - for i := 0; ; { - if console.Buffered() > 0 { - data, _ := console.ReadByte() - if debug { - fmt.Printf("\rdata: %x\r\n\r", data) - prompt() - console.Write(input[:i]) - } - switch state { - case StateInput: - switch data { - case 0x8: - fallthrough - case 0x7f: // this is probably wrong... works on my machine tho :) - // backspace - if i > 0 { - i -= 1 - console.Write([]byte{0x8, 0x20, 0x8}) - } - case 13: - // return key - console.Write([]byte("\r\n")) - runCommand(string(input[:i])) - prompt() - - i = 0 - continue - case 27: - // escape - state = StateEscape - default: - // anything else, just echo the character if it is printable - if strconv.IsPrint(rune(data)) { - if i < (consoleBufLen - 1) { - console.WriteByte(data) - input[i] = data - i++ - } - } - } - case StateEscape: - switch data { - case 0x5b: - state = StateEscBrc - default: - state = StateInput - } - default: - // TODO: handle escape sequences - state = StateInput - } - //time.Sleep(10 * time.Millisecond) - } - } -} - -func runCommand(line string) { - argv := strings.SplitN(strings.TrimSpace(line), " ", -1) - cmd := argv[0] - cmdfn, ok := commands[cmd] - if !ok { - println("unknown command: " + line) - return - } - cmdfn(argv) -} - -func noop(argv []string) {} - -func dbg(argv []string) { - if debug { - debug = false - println("Console debugging off") - } else { - debug = true - println("Console debugging on") - } -} - -func lsblk(argv []string) { - id, _ := dev.ReadJEDEC() - status, _ := dev.ReadStatus() - serialNumber1, _ := dev.ReadSerialNumber() - fmt.Printf( - "\n-------------------------------------\r\n"+ - " Device Information: \r\n"+ - "-------------------------------------\r\n"+ - " JEDEC ID: %v\r\n"+ - " Serial: %v\r\n"+ - " Status: %2x\r\n"+ - "-------------------------------------\r\n\r\n", - id, - serialNumber1, - status, - ) -} - -func xxd(argv []string) { - var err error - var addr uint64 = 0x0 - var size int = 64 - switch len(argv) { - case 3: - if size, err = strconv.Atoi(argv[2]); err != nil { - println("Invalid size argument: " + err.Error() + "\r\n") - return - } - if size > storageBufLen || size < 1 { - fmt.Printf("Size of hexdump must be greater than 0 and less than %d\r\n", storageBufLen) - return - } - fallthrough - case 2: - /* - if argv[1][:2] != "0x" { - println("Invalid hex address (should start with 0x)") - return - } - */ - if addr, err = strconv.ParseUint(argv[1], 16, 32); err != nil { - println("Invalid address: " + err.Error() + "\r\n") - return - } - fallthrough - case 1: - // no args supplied, so nothing to do here, just use the defaults - default: - println("usage: xxd \r\n") - return - } - buf := store[0:size] - //fatdisk.ReadAt(buf, int64(addr)) - dev.ReadBuffer(uint32(addr), buf) - xxdfprint(os.Stdout, uint32(addr), buf) -} - -func xxdfprint(w io.Writer, offset uint32, b []byte) { - var l int - var buf16 = make([]byte, 16) - for i, c := 0, len(b); i < c; i += 16 { - l = i + 16 - if l >= c { - l = c - } - fmt.Fprintf(w, "%08x: % x ", offset+uint32(i), b[i:l]) - for j, n := 0, l-i; j < 16; j++ { - if j >= n || !strconv.IsPrint(rune(b[i+j])) { - buf16[j] = '.' - } else { - buf16[j] = b[i+j] - } - } - console.Write(buf16) - println() - // "%s\r\n", b[i:l], "") - } -} - -func write(s string) { - println(s) -} - -func prompt() { - print("==> ") -} diff --git a/flash/devices.go b/flash/devices.go new file mode 100644 index 000000000..4a2e6d76b --- /dev/null +++ b/flash/devices.go @@ -0,0 +1,432 @@ +package flash + +import "time" + +type DeviceIdentifier interface { + Identify(id JedecID) Attrs +} + +type DeviceIdentifierFunc func(id JedecID) Attrs + +func (fn DeviceIdentifierFunc) Identify(id JedecID) Attrs { + return fn(id) +} + +var DefaultDeviceIdentifier = DeviceIdentifierFunc(func(id JedecID) Attrs { + switch id.Uint32() { + case 0x010617: + return S25FL064L() + case 0x014015: + return S25FL216K() + case 0x1F4501: + return AT25DF081A() + case 0xC22015: + return MX25L1606() + case 0xC22016: + return MX25L3233F() + case 0xC22817: + return MX25R6435F() + case 0xC84015: + return GD25Q16C() + case 0xC84017: + return GD25Q64C() + case 0xEF4015: + return W25Q16JVIQ() + case 0xEF4016: + return W25Q32FV() + case 0xEF4017: + return W25Q64JVIQ() + case 0xEF4018: + return W25Q128JVSQ() + case 0xEF6014: + return W25Q80DL() + case 0xEF6015: + return W25Q16FW() + case 0xEF6016: + return W25Q32BV() + case 0xEF7015: + return W25Q16JVIM() + case 0xEF7016: + return W25Q32JVIM() + case 0xEF7017: + return W25Q64JVIM() + case 0xEF7018: + return W25Q128JVPM() + default: + return Attrs{JedecID: id} + } +}) + +// Settings for the Cypress (was Spansion) S25FL064L 8MiB SPI flash. +// Datasheet: http://www.cypress.com/file/316661/download +func S25FL064L() Attrs { + return Attrs{ + TotalSize: 1 << 23, // 8 MiB + StartUp: 300 * time.Microsecond, + JedecID: JedecID{0x01, 0x60, 0x17}, + MaxClockSpeedMHz: 108, + QuadEnableBitMask: 0x02, + HasSectorProtection: false, + SupportsFastRead: true, + SupportsQSPI: true, + SupportsQSPIWrites: true, + WriteStatusSplit: false, + SingleStatusByte: false, + } +} + +// Settings for the Cypress (was Spansion) S25FL116K 2MiB SPI flash. +// Datasheet: http://www.cypress.com/file/196886/download +func S25FL116K() Attrs { + return Attrs{ + TotalSize: 1 << 21, // 2 MiB + StartUp: 10000 * time.Microsecond, + JedecID: JedecID{0x01, 0x40, 0x15}, + MaxClockSpeedMHz: 108, + QuadEnableBitMask: 0x02, + HasSectorProtection: false, + SupportsFastRead: true, + SupportsQSPI: true, + SupportsQSPIWrites: false, + WriteStatusSplit: false, + SingleStatusByte: false, + } +} + +// Settings for the Cypress (was Spansion) S25FL216K 2MiB SPI flash. +// Datasheet: http://www.cypress.com/file/197346/download +func S25FL216K() Attrs { + return Attrs{ + TotalSize: 1 << 21, // 2 MiB + StartUp: 10000 * time.Microsecond, + JedecID: JedecID{0x01, 0x40, 0x15}, + MaxClockSpeedMHz: 65, + QuadEnableBitMask: 0x02, + HasSectorProtection: false, + SupportsFastRead: true, + SupportsQSPI: true, + SupportsQSPIWrites: false, + WriteStatusSplit: false, + SingleStatusByte: false, + } +} + +// Settings for the Adesto Tech AT25DF081A 1MiB SPI flash. Its on the SAMD21 +// Xplained board. +// Datasheet: https://www.adestotech.com/wp-content/uploads/doc8715.pdf +func AT25DF081A() Attrs { + return Attrs{ + TotalSize: 1 << 20, // 1 MiB + StartUp: 10000 * time.Microsecond, + JedecID: JedecID{0x1F, 0x45, 0x01}, + MaxClockSpeedMHz: 85, + QuadEnableBitMask: 0x00, + HasSectorProtection: true, + SupportsFastRead: true, + SupportsQSPI: false, + SupportsQSPIWrites: false, + WriteStatusSplit: false, + SingleStatusByte: false, + } +} + +// Settings for the Macronix MX25L1606 2MiB SPI flash. +// Datasheet: +func MX25L1606() Attrs { + return Attrs{ + TotalSize: 1 << 21, // 2 MiB, + StartUp: 5000 * time.Microsecond, + JedecID: JedecID{0xC2, 0x20, 0x15}, + MaxClockSpeedMHz: 8, + QuadEnableBitMask: 0x40, + HasSectorProtection: false, + SupportsFastRead: true, + SupportsQSPI: true, + SupportsQSPIWrites: true, + WriteStatusSplit: false, + SingleStatusByte: true, + } +} + +// Settings for the Macronix MX25L3233F 4MiB SPI flash. +// Datasheet: +// http://www.macronix.com/Lists/Datasheet/Attachments/7426/MX25L3233F,%203V,%2032Mb,%20v1.6.pdf +func MX25L3233F() Attrs { + return Attrs{ + TotalSize: 1 << 22, // 4 MiB + StartUp: 5000 * time.Microsecond, + JedecID: JedecID{0xC2, 0x20, 0x16}, + MaxClockSpeedMHz: 133, + QuadEnableBitMask: 0x40, + HasSectorProtection: false, + SupportsFastRead: true, + SupportsQSPI: true, + SupportsQSPIWrites: true, + WriteStatusSplit: false, + SingleStatusByte: false, + } +} + +// Settings for the Macronix MX25R6435F 8MiB SPI flash. +// Datasheet: +// http://www.macronix.com/Lists/Datasheet/Attachments/7428/MX25R6435F,%20Wide%20Range,%2064Mb,%20v1.4.pdf +// By default its in lower power mode which can only do 8mhz. In high power mode +// it can do 80mhz. +func MX25R6435F() Attrs { + return Attrs{ + TotalSize: 1 << 23, // 8 MiB + StartUp: 5000 * time.Microsecond, + JedecID: JedecID{0xC2, 0x28, 0x17}, + MaxClockSpeedMHz: 8, + QuadEnableBitMask: 0x40, + HasSectorProtection: false, + SupportsFastRead: true, + SupportsQSPI: true, + SupportsQSPIWrites: true, + WriteStatusSplit: false, + SingleStatusByte: true, + } +} + +// Settings for the Gigadevice GD25Q16C 2MiB SPI flash. +// Datasheet: http://www.gigadevice.com/datasheet/gd25q16c/ +func GD25Q16C() Attrs { + return Attrs{ + TotalSize: 1 << 21, // 2 MiB + StartUp: 5000 * time.Microsecond, + JedecID: JedecID{0xC8, 0x40, 0x15}, + MaxClockSpeedMHz: 104, + QuadEnableBitMask: 0x20, + SupportsFastRead: true, + SupportsQSPI: true, + SupportsQSPIWrites: true, + WriteStatusSplit: false, + SingleStatusByte: false, + } +} + +// Settings for the Gigadevice GD25Q64C 8MiB SPI flash. +// Datasheet: http://www.elm-tech.com/en/products/spi-flash-memory/gd25q64/gd25q64.pdf +func GD25Q64C() Attrs { + return Attrs{ + TotalSize: 1 << 23, // 8 MiB + StartUp: 5000 * time.Microsecond, + JedecID: JedecID{0xC8, 0x40, 0x17}, + MaxClockSpeedMHz: 104, + QuadEnableBitMask: 0x02, + HasSectorProtection: false, + SupportsFastRead: true, + SupportsQSPI: true, + SupportsQSPIWrites: true, + WriteStatusSplit: true, + SingleStatusByte: false, + } +} + +// Settings for the Winbond W25Q16JV-IQ 2MiB SPI flash. Note that JV-IM has a +// different .memory_type (0x70) Datasheet: +// https://www.winbond.com/resource-files/w25q16jv%20spi%20revf%2005092017.pdf +func W25Q16JVIQ() Attrs { + return Attrs{ + TotalSize: 1 << 21, // 2 MiB + StartUp: 5000 * time.Microsecond, + JedecID: JedecID{0xEF, 0x40, 0x15}, + MaxClockSpeedMHz: 133, + QuadEnableBitMask: 0x02, + HasSectorProtection: false, + SupportsFastRead: true, + SupportsQSPI: true, + SupportsQSPIWrites: true, + WriteStatusSplit: false, + SingleStatusByte: false, + } +} + +// Settings for the Winbond W25Q16FW 2MiB SPI flash. +// Datasheet: +// https://www.winbond.com/resource-files/w25q16fw%20revj%2005182017%20sfdp.pdf +func W25Q16FW() Attrs { + return Attrs{ + TotalSize: 1 << 21, // 2 MiB + StartUp: 5000 * time.Microsecond, + JedecID: JedecID{0xEF, 0x60, 0x15}, + MaxClockSpeedMHz: 133, + QuadEnableBitMask: 0x02, + HasSectorProtection: false, + SupportsFastRead: true, + SupportsQSPI: true, + SupportsQSPIWrites: true, + WriteStatusSplit: false, + SingleStatusByte: false, + } +} + +// Settings for the Winbond W25Q16JV-IM 2MiB SPI flash. Note that JV-IQ has a +// different .memory_type (0x40) Datasheet: +// https://www.winbond.com/resource-files/w25q16jv%20spi%20revf%2005092017.pdf +func W25Q16JVIM() Attrs { + return Attrs{ + TotalSize: 1 << 21, // 2 MiB + StartUp: 5000 * time.Microsecond, + JedecID: JedecID{0xEF, 0x70, 0x15}, + MaxClockSpeedMHz: 133, + QuadEnableBitMask: 0x02, + HasSectorProtection: false, + SupportsFastRead: true, + SupportsQSPI: true, + SupportsQSPIWrites: true, + WriteStatusSplit: false, + SingleStatusByte: false, + } +} + +// Settings for the Winbond W25Q32BV 4MiB SPI flash. +// Datasheet: +// https://www.winbond.com/resource-files/w25q32bv_revi_100413_wo_automotive.pdf +func W25Q32BV() Attrs { + return Attrs{ + TotalSize: 1 << 22, // 4 MiB + StartUp: 10000 * time.Microsecond, + JedecID: JedecID{0xEF, 0x60, 0x16}, + MaxClockSpeedMHz: 104, + QuadEnableBitMask: 0x02, + HasSectorProtection: false, + SupportsFastRead: true, + SupportsQSPI: true, + SupportsQSPIWrites: false, + WriteStatusSplit: false, + SingleStatusByte: false, + } +} + +// Settings for the Winbond W25Q32JV-IM 4MiB SPI flash. +// Datasheet: +// https://www.winbond.com/resource-files/w25q32jv%20revg%2003272018%20plus.pdf +func W25Q32JVIM() Attrs { + return Attrs{ + TotalSize: 1 << 22, // 4 MiB + StartUp: 5000 * time.Microsecond, + JedecID: JedecID{0xEF, 0x70, 0x16}, + MaxClockSpeedMHz: 133, + QuadEnableBitMask: 0x02, + HasSectorProtection: false, + SupportsFastRead: true, + SupportsQSPI: true, + SupportsQSPIWrites: true, + WriteStatusSplit: false, + SingleStatusByte: false, + } +} + +// Settings for the Winbond W25Q64JV-IM 8MiB SPI flash. Note that JV-IQ has a +// different .memory_type (0x40) Datasheet: +// http://www.winbond.com/resource-files/w25q64jv%20revj%2003272018%20plus.pdf +func W25Q64JVIM() Attrs { + return Attrs{ + TotalSize: 1 << 23, // 8 MiB + StartUp: 5000 * time.Microsecond, + JedecID: JedecID{0xEF, 0x70, 0x17}, + MaxClockSpeedMHz: 133, + QuadEnableBitMask: 0x02, + HasSectorProtection: false, + SupportsFastRead: true, + SupportsQSPI: true, + SupportsQSPIWrites: true, + WriteStatusSplit: false, + SingleStatusByte: false, + } +} + +// Settings for the Winbond W25Q64JV-IQ 8MiB SPI flash. Note that JV-IM has a +// different .memory_type (0x70) Datasheet: +// http://www.winbond.com/resource-files/w25q64jv%20revj%2003272018%20plus.pdf +func W25Q64JVIQ() Attrs { + return Attrs{ + TotalSize: 1 << 23, // 8 MiB + StartUp: 5000 * time.Microsecond, + JedecID: JedecID{0xEF, 0x40, 0x17}, + MaxClockSpeedMHz: 133, + QuadEnableBitMask: 0x02, + HasSectorProtection: false, + SupportsFastRead: true, + SupportsQSPI: true, + SupportsQSPIWrites: true, + WriteStatusSplit: false, + SingleStatusByte: false, + } +} + +// Settings for the Winbond W25Q80DL 1MiB SPI flash. +// Datasheet: +// https://www.winbond.com/resource-files/w25q80dv%20dl_revh_10022015.pdf +func W25Q80DL() Attrs { + return Attrs{ + TotalSize: 1 << 20, // 1 MiB + StartUp: 5000 * time.Microsecond, + JedecID: JedecID{0xEF, 0x60, 0x14}, + MaxClockSpeedMHz: 104, + QuadEnableBitMask: 0x02, + HasSectorProtection: false, + SupportsFastRead: true, + SupportsQSPI: true, + SupportsQSPIWrites: false, + WriteStatusSplit: false, + SingleStatusByte: false, + } +} + +// Settings for the Winbond W25Q128JV-SQ 16MiB SPI flash. Note that JV-IM has a +// different .memory_type (0x70) Datasheet: +// https://www.winbond.com/resource-files/w25q128jv%20revf%2003272018%20plus.pdf +func W25Q128JVSQ() Attrs { + return Attrs{ + TotalSize: 1 << 24, // 16 MiB + StartUp: 5000 * time.Microsecond, + JedecID: JedecID{0xEF, 0x40, 0x18}, + MaxClockSpeedMHz: 133, + QuadEnableBitMask: 0x02, + HasSectorProtection: false, + SupportsFastRead: true, + SupportsQSPI: true, + SupportsQSPIWrites: true, + WriteStatusSplit: false, + SingleStatusByte: false, + } +} + +// Settings for the Winbond W25Q128JV-PM 16MiB SPI flash. Note that JV-IM has a +// different .memory_type (0x70) Datasheet: +// https://www.winbond.com/resource-files/w25q128jv%20revf%2003272018%20plus.pdf +func W25Q128JVPM() Attrs { + return Attrs{ + TotalSize: 1 << 24, // 16 MiB + StartUp: 5000 * time.Microsecond, + JedecID: JedecID{0xEF, 0x70, 0x18}, + MaxClockSpeedMHz: 133, + QuadEnableBitMask: 0x02, + HasSectorProtection: false, + SupportsFastRead: true, + SupportsQSPI: true, + SupportsQSPIWrites: true, + WriteStatusSplit: false, + SingleStatusByte: false, + } +} + +// Settings for the Winbond W25Q32FV 4MiB SPI flash. +// Datasheet:http://www.winbond.com/resource-files/w25q32fv%20revj%2006032016.pdf?__locale=en +func W25Q32FV() Attrs { + return Attrs{ + TotalSize: 1 << 22, // 4 MiB + StartUp: 5000 * time.Microsecond, + JedecID: JedecID{0xEF, 0x40, 0x16}, + MaxClockSpeedMHz: 104, + QuadEnableBitMask: 0x00, + HasSectorProtection: false, + SupportsFastRead: true, + SupportsQSPI: false, + SupportsQSPIWrites: false, + WriteStatusSplit: false, + SingleStatusByte: false, + } +} diff --git a/flash/flash.go b/flash/flash.go index 7cdaf877a..79a99b657 100644 --- a/flash/flash.go +++ b/flash/flash.go @@ -60,11 +60,11 @@ type JedecID struct { Capacity uint8 } -func (id *JedecID) Uint32() uint32 { +func (id JedecID) Uint32() uint32 { return uint32(id.ManufID)<<16 | uint32(id.MemType)<<8 | uint32(id.Capacity) } -func (id *JedecID) String() string { +func (id JedecID) String() string { return fmt.Sprintf("%06X", id.Uint32()) } @@ -79,9 +79,13 @@ type Device struct { attrs Attrs } +type DeviceConfig struct { + Identifier DeviceIdentifier +} + type Attrs struct { TotalSize uint32 - //start_up_time_us uint16 + StartUp time.Duration // Three response bytes to 0x9f JEDEC ID command. JedecID @@ -107,7 +111,7 @@ type Attrs struct { // Requires a separate command 0x31 to write to the second byte of the status // register. Otherwise two byte are written via 0x01. - WriteStatusRegisterSplit bool + WriteStatusSplit bool // True when the status register is a single byte. This implies the Quad // Enable bit is in the first byte and the Read Status Register 2 command @@ -115,17 +119,21 @@ type Attrs struct { SingleStatusByte bool } -func (dev *Device) Begin() (err error) { +func (dev *Device) Configure(config *DeviceConfig) (err error) { - dev.transport.begin() + dev.transport.configure(config) - // TODO: should check JEDEC ID against list of known devices - /* - if dev.ID, err = dev.ReadJEDEC(); err != nil { - return err - } - println("JEDEC:", dev.ID.String()) - */ + var id JedecID + if id, err = dev.ReadJEDEC(); err != nil { + return err + } + + if config.Identifier != nil { + dev.attrs = config.Identifier.Identify(id) + } else { + dev.attrs = Attrs{JedecID: id} + //panic("what to do when identifier is nil???") + } // We don't know what state the flash is in so wait for any remaining writes and then reset. diff --git a/flash/transport_qspi_samd.go b/flash/transport_qspi_samd.go index f94c2f761..5155b76a0 100644 --- a/flash/transport_qspi_samd.go +++ b/flash/transport_qspi_samd.go @@ -42,7 +42,7 @@ type qspiTransport struct { d3 machine.Pin } -func (q qspiTransport) begin() { +func (q qspiTransport) configure(config *DeviceConfig) { // enable main clocks sam.MCLK.APBCMASK.SetBits(sam.MCLK_APBCMASK_QSPI_) diff --git a/flash/transport_spi.go b/flash/transport_spi.go index 4ce109d8c..d10ab89d0 100644 --- a/flash/transport_spi.go +++ b/flash/transport_spi.go @@ -3,7 +3,7 @@ package flash import "machine" type transport interface { - begin() + configure(config *DeviceConfig) supportQuadMode() bool setClockSpeed(hz uint32) (err error) runCommand(cmd byte) (err error) @@ -36,7 +36,7 @@ type spiTransport struct { ss machine.Pin } -func (tr *spiTransport) begin() { +func (tr *spiTransport) configure(config *DeviceConfig) { // Configure spi bus tr.setClockSpeed(5000000) From 3e9704dab51412458148b4822f1d2a8ec3cf519e Mon Sep 17 00:00:00 2001 From: BCG Date: Sat, 15 Feb 2020 02:42:29 -0500 Subject: [PATCH 09/18] Added more comments and also checks for device-specific attributes --- examples/flash/console/console_example.go | 52 ++++-- flash/devices.go | 18 +- flash/flash.go | 217 ++++++++++++++-------- flash/transport_qspi_samd.go | 19 +- flash/transport_spi.go | 2 +- 5 files changed, 204 insertions(+), 104 deletions(-) diff --git a/examples/flash/console/console_example.go b/examples/flash/console/console_example.go index fd4e874c2..8840ee5f9 100644 --- a/examples/flash/console/console_example.go +++ b/examples/flash/console/console_example.go @@ -26,7 +26,6 @@ var ( commands map[string]cmdfunc = map[string]cmdfunc{ "": cmdfunc(noop), - "dbg": cmdfunc(dbg), "erase": cmdfunc(erase), "lsblk": cmdfunc(lsblk), "write": cmdfunc(write), @@ -122,37 +121,46 @@ func runCommand(line string) { func noop(argv []string) {} -func dbg(argv []string) { - if debug { - debug = false - println("Console debugging off") - } else { - debug = true - println("Console debugging on") - } -} - func lsblk(argv []string) { - id, _ := dev.ReadJEDEC() - status, _ := dev.ReadStatus() + attrs := dev.Attrs() + status1, _ := dev.ReadStatus() + status2, _ := dev.ReadStatus2() serialNumber1, _ := dev.ReadSerialNumber() fmt.Printf( "\n-------------------------------------\r\n"+ " Device Information: \r\n"+ "-------------------------------------\r\n"+ " JEDEC ID: %v\r\n"+ - " Serial: %v\r\n"+ - " Status: %2x\r\n"+ + " Serial: %v\r\n"+ + " Status 1: %02x\r\n"+ + " Status 2: %02x\r\n"+ + " \r\n"+ + " Max clock speed (MHz): %d\r\n"+ + " Has Sector Protection: %t\r\n"+ + " Supports Fast Reads: %t\r\n"+ + " Supports QSPI Reads: %t\r\n"+ + " Supports QSPI Write: %t\r\n"+ + " Write Status Split: %t\r\n"+ + " Single Status Byte: %t\r\n"+ "-------------------------------------\r\n\r\n", - id, + attrs.JedecID, serialNumber1, - status, + status1, + status2, + attrs.MaxClockSpeedMHz, + attrs.HasSectorProtection, + attrs.SupportsFastRead, + attrs.SupportsQSPI, + attrs.SupportsQSPIWrites, + attrs.WriteStatusSplit, + attrs.SingleStatusByte, ) } func erase(argv []string) { if len(argv) < 3 { - println("usage: erase ") + println("usage: erase ") + return } var err error var addr uint64 = 0x0 @@ -166,10 +174,14 @@ func erase(argv []string) { } } else if argv[1] == "sector" { if err = dev.EraseSector(uint32(addr)); err != nil { - println("Block erase error: " + err.Error() + "\r\n") + println("Sector erase error: " + err.Error() + "\r\n") + } + } else if argv[1] == "chip" { + if err = dev.EraseChip(); err != nil { + println("Chip erase error: " + err.Error() + "\r\n") } } else { - println("usage: erase ") + println("usage: erase ") } } diff --git a/flash/devices.go b/flash/devices.go index 4a2e6d76b..69239a62f 100644 --- a/flash/devices.go +++ b/flash/devices.go @@ -2,16 +2,32 @@ package flash import "time" +// A DeviceIdentifier can be passed to the Configure() method of a flash Device +// in order provide a means of discovery of device-specific attributes based on +// the JEDEC ID read from the device. type DeviceIdentifier interface { + // Identify returns an Attrs struct based on the provided JEDEC ID Identify(id JedecID) Attrs } +// DeviceIdentifierFunc is a functional Identifier implementation type DeviceIdentifierFunc func(id JedecID) Attrs +// Identify implements the Identifier interface func (fn DeviceIdentifierFunc) Identify(id JedecID) Attrs { return fn(id) } +// DefaultDeviceIndentifier is a DeviceIdentifier that is capable of recognizing +// JEDEC IDs for all of the known memory devices in this package. If you are +// have no way to be sure about the type of memory device that might be on a +// board you are targeting, this can be a good starting point to use. The +// downside of using this function is that it will prevent the compiler from +// being able to mark any of the functions for the various devices as unused, +// resulting in larger code size. If code size is a concern, and if you know +// ahead of time you are only dealing with a limited set of memory devices, it +// might be worthwhile to use your own implementation of a DeviceIdentifier +// that only references those devices, so that more methods are marked unused. var DefaultDeviceIdentifier = DeviceIdentifierFunc(func(id JedecID) Attrs { switch id.Uint32() { case 0x010617: @@ -196,7 +212,7 @@ func GD25Q16C() Attrs { StartUp: 5000 * time.Microsecond, JedecID: JedecID{0xC8, 0x40, 0x15}, MaxClockSpeedMHz: 104, - QuadEnableBitMask: 0x20, + QuadEnableBitMask: 0x02, SupportsFastRead: true, SupportsQSPI: true, SupportsQSPIWrites: true, diff --git a/flash/flash.go b/flash/flash.go index 79a99b657..c2dd47c32 100644 --- a/flash/flash.go +++ b/flash/flash.go @@ -5,25 +5,6 @@ import ( "time" ) -const ( - cmdRead = 0x03 // read memory using single-bit transfer - cmdQuadRead = 0x6B // read with 1 line address, 4 line data - cmdReadJedecID = 0x9F // read the JEDEC ID from the device - cmdPageProgram = 0x02 // write a page of memory using single-bit transfer - cmdQuadPageProgram = 0x32 // write with 1 line address, 4 line data - cmdReadStatus = 0x05 // read status register 1 - cmdReadStatus2 = 0x35 // read status register 2 - cmdWriteStatus = 0x01 // write status register 1 - cmdWriteStatus2 = 0x31 // write status register 2 - cmdEnableReset = 0x66 // enable reset - cmdReset = 0x99 // perform reset - cmdWriteEnable = 0x06 // write-enable memory - cmdWriteDisable = 0x04 // write-protect memory - cmdEraseSector = 0x20 // erase a sector of memory - cmdEraseBlock = 0xD8 // erase a block of memory - cmdEraseChip = 0xC7 // erase the entire chip -) - const ( // BlockSize is the number of bytes in a block for most/all NOR flash memory BlockSize = 64 * 1024 @@ -35,54 +16,43 @@ const ( PageSize = 256 ) -type Error uint8 - -const ( - _ = iota - ErrInvalidClockSpeed Error = iota - ErrInvalidAddrRange -) +// Device represents a NOR flash memory device accessible using SPI +type Device struct { + trans transport + attrs Attrs +} -func (err Error) Error() string { - switch err { - case ErrInvalidClockSpeed: - return "flash: invalid clock speed" - case ErrInvalidAddrRange: - return "flash: invalid address range" - default: - return "flash: unspecified error" - } +// DeviceConfig contains the parameters that can be set when configuring a +// flash memory device. +type DeviceConfig struct { + Identifier DeviceIdentifier } +// JedecID encapsules the ID values that unique identify a flash memory device. type JedecID struct { ManufID uint8 MemType uint8 Capacity uint8 } +// Uint32 returns the JEDEC ID packed into a uint32 func (id JedecID) Uint32() uint32 { return uint32(id.ManufID)<<16 | uint32(id.MemType)<<8 | uint32(id.Capacity) } +// String implements the Stringer interface to print out the hex value of the ID func (id JedecID) String() string { return fmt.Sprintf("%06X", id.Uint32()) } +// SerialNumber represents a serial number read from a flash memory device type SerialNumber uint64 +// String implements the Stringer interface to print out the hex value of the SN func (sn SerialNumber) String() string { return fmt.Sprintf("%8X", uint64(sn)) } -type Device struct { - transport transport - attrs Attrs -} - -type DeviceConfig struct { - Identifier DeviceIdentifier -} - type Attrs struct { TotalSize uint32 StartUp time.Duration @@ -121,72 +91,126 @@ type Attrs struct { func (dev *Device) Configure(config *DeviceConfig) (err error) { - dev.transport.configure(config) + dev.trans.configure(config) var id JedecID if id, err = dev.ReadJEDEC(); err != nil { return err } + // try to ascertain the vendor-specific attributes of the chip using the + // provided Identifier if config.Identifier != nil { dev.attrs = config.Identifier.Identify(id) } else { dev.attrs = Attrs{JedecID: id} - //panic("what to do when identifier is nil???") } - // We don't know what state the flash is in so wait for any remaining writes and then reset. - - var s byte // status + // We don't know what state the flash is in so wait for any remaining + // writes and then reset. // The write in progress bit should be low. - for s, err = dev.ReadStatus(); (s & 0x01) > 0; s, err = dev.ReadStatus() { + for s, err := dev.ReadStatus(); (s & 0x01) > 0; s, err = dev.ReadStatus() { if err != nil { return err } } - // The suspended write/erase bit should be low. - for s, err = dev.ReadStatus2(); (s & 0x80) > 0; s, err = dev.ReadStatus2() { + for s, err := dev.ReadStatus2(); (s & 0x80) > 0; s, err = dev.ReadStatus2() { if err != nil { return err } } - - if err = dev.transport.runCommand(cmdEnableReset); err != nil { + // perform device reset + if err := dev.trans.runCommand(cmdEnableReset); err != nil { return err } - if err = dev.transport.runCommand(cmdReset); err != nil { + if err := dev.trans.runCommand(cmdReset); err != nil { return err } - // Wait 30us for the reset - stop := time.Now().UnixNano() + int64(30*time.Microsecond) - for stop > time.Now().UnixNano() { + // Wait for the reset - 30us by default + dur := int64(dev.attrs.StartUp) + if dur == 0 { + dur = int64(30 * time.Microsecond) + } + for stop := time.Now().UnixNano() + dur; stop > time.Now().UnixNano(); { } // Speed up to max device frequency - //_trans->setClockSpeed(_flash_dev->max_clock_speed_mhz*1000000UL); + if dev.attrs.MaxClockSpeedMHz > 0 { + //dev.trans.setClockSpeed(uint32(dev.attrs.MaxClockSpeedMHz) * 1e6) + } - if err = dev.transport.runCommand(cmdWriteDisable); err != nil { + // Enable Quad Mode if available + if dev.trans.supportQuadMode() && dev.attrs.QuadEnableBitMask > 0 { + // Verify that QSPI mode is enabled. + var status byte + if dev.attrs.SingleStatusByte { + status, err = dev.ReadStatus() + } else { + status, err = dev.ReadStatus2() + } + if err != nil { + return err + } + // Check and set the quad enable bit. + if status&dev.attrs.QuadEnableBitMask == 0 { + if err := dev.WriteEnable(); err != nil { + return err + } + fullStatus := []byte{0x00, dev.attrs.QuadEnableBitMask} + if dev.attrs.WriteStatusSplit { + err = dev.trans.writeCommand(cmdWriteStatus2, fullStatus[1:]) + } else if dev.attrs.SingleStatusByte { + err = dev.trans.writeCommand(cmdWriteStatus, fullStatus[1:]) + } else { + err = dev.trans.writeCommand(cmdWriteStatus, fullStatus) + } + if err != nil { + return err + } + } + } + + // disable sector protection if the chip has it + if dev.attrs.HasSectorProtection { + if err := dev.WriteEnable(); err != nil { + return err + } + if err := dev.trans.writeCommand(cmdWriteStatus, []byte{0x00}); err != nil { + return err + } + } + + // write disable + if err := dev.trans.runCommand(cmdWriteDisable); err != nil { return err } + return dev.WaitUntilReady() +} - err = dev.WaitUntilReady() - return err +// Attrs returns the attributes of the device determined from the most recent +// call to Configure(). If no call to Configure() has been made, this will be +// the zero value of the Attrs struct. +func (dev *Device) Attrs() Attrs { + return dev.attrs } +// ReadJEDEC reads the JEDEC ID from the device; this ID can then be used to +// ascertain the attributes of the chip from a list of known devices. func (dev *Device) ReadJEDEC() (JedecID, error) { jedecID := make([]byte, 3) - if err := dev.transport.readCommand(cmdReadJedecID, jedecID); err != nil { + if err := dev.trans.readCommand(cmdReadJedecID, jedecID); err != nil { return JedecID{}, err } return JedecID{jedecID[0], jedecID[1], jedecID[2]}, nil } +// ReadSerialNumber reads the serial numbers from the connected device. func (dev *Device) ReadSerialNumber() (SerialNumber, error) { sn := make([]byte, 12) - if err := dev.transport.readCommand(0x4B, sn); err != nil { + if err := dev.trans.readCommand(0x4B, sn); err != nil { return 0, err } return SerialNumber(uint64(sn[11]) | uint64(sn[10])<<0x8 | @@ -194,14 +218,17 @@ func (dev *Device) ReadSerialNumber() (SerialNumber, error) { uint64(sn[6])<<0x28 | uint64(sn[5])<<0x30 | uint64(sn[4])<<0x38), nil } +// ReadBuffer fills the provided buffer with memory read from the device +// starting at the provided address. func (dev *Device) ReadBuffer(addr uint32, buf []byte) error { - // TODO: check if Begin() was successful if err := dev.WaitUntilReady(); err != nil { return err } - return dev.transport.readMemory(addr, buf) + return dev.trans.readMemory(addr, buf) } +// WriteBuffer writes data to the device, one page at a time, starting at the +// provided address. This method assumes that the destination is already erased. func (dev *Device) WriteBuffer(addr uint32, buf []byte) (n int, err error) { remain := uint32(len(buf)) idx := uint32(0) @@ -217,7 +244,7 @@ func (dev *Device) WriteBuffer(addr uint32, buf []byte) (n int, err error) { if leftOnPage < remain { toWrite = leftOnPage } - if err = dev.transport.writeMemory(addr, buf[idx:idx+toWrite]); err != nil { + if err = dev.trans.writeMemory(addr, buf[idx:idx+toWrite]); err != nil { return } idx += toWrite @@ -228,7 +255,7 @@ func (dev *Device) WriteBuffer(addr uint32, buf []byte) (n int, err error) { } func (dev *Device) WriteEnable() error { - return dev.transport.runCommand(cmdWriteEnable) + return dev.trans.runCommand(cmdWriteEnable) } // EraseBlock erases a block of memory at the specified address @@ -239,7 +266,7 @@ func (dev *Device) EraseBlock(addr uint32) error { if err := dev.WriteEnable(); err != nil { return err } - return dev.transport.eraseCommand(cmdEraseBlock, addr*BlockSize) + return dev.trans.eraseCommand(cmdEraseBlock, addr*BlockSize) } // EraseSector erases a sector of memory at the specified address @@ -250,7 +277,7 @@ func (dev *Device) EraseSector(addr uint32) error { if err := dev.WriteEnable(); err != nil { return err } - return dev.transport.eraseCommand(cmdEraseSector, addr*SectorSize) + return dev.trans.eraseCommand(cmdEraseSector, addr*SectorSize) } // EraseChip erases the entire flash memory chip @@ -261,20 +288,20 @@ func (dev *Device) EraseChip() error { if err := dev.WriteEnable(); err != nil { return err } - return dev.transport.runCommand(cmdEraseChip) + return dev.trans.runCommand(cmdEraseChip) } // ReadStatus reads the value from status register 1 of the device func (dev *Device) ReadStatus() (status byte, err error) { buf := make([]byte, 1) - err = dev.transport.readCommand(cmdReadStatus, buf) + err = dev.trans.readCommand(cmdReadStatus, buf) return buf[0], err } // ReadStatus2 reads the value from status register 2 of the device func (dev *Device) ReadStatus2() (status byte, err error) { buf := make([]byte, 1) - err = dev.transport.readCommand(cmdReadStatus2, buf) + err = dev.trans.readCommand(cmdReadStatus2, buf) return buf[0], err } @@ -284,9 +311,47 @@ func (dev *Device) WaitUntilReady() error { if err != nil { return err } - if time.Now().UnixNano() > expire { - return fmt.Errorf("WaitUntilReady expired") - } + } + if time.Now().UnixNano() > expire { + return fmt.Errorf("WaitUntilReady expired") } return nil } + +const ( + cmdRead = 0x03 // read memory using single-bit transfer + cmdQuadRead = 0x6B // read with 1 line address, 4 line data + cmdReadJedecID = 0x9F // read the JEDEC ID from the device + cmdPageProgram = 0x02 // write a page of memory using single-bit transfer + cmdQuadPageProgram = 0x32 // write with 1 line address, 4 line data + cmdReadStatus = 0x05 // read status register 1 + cmdReadStatus2 = 0x35 // read status register 2 + cmdWriteStatus = 0x01 // write status register 1 + cmdWriteStatus2 = 0x31 // write status register 2 + cmdEnableReset = 0x66 // enable reset + cmdReset = 0x99 // perform reset + cmdWriteEnable = 0x06 // write-enable memory + cmdWriteDisable = 0x04 // write-protect memory + cmdEraseSector = 0x20 // erase a sector of memory + cmdEraseBlock = 0xD8 // erase a block of memory + cmdEraseChip = 0xC7 // erase the entire chip +) + +type Error uint8 + +const ( + _ = iota + ErrInvalidClockSpeed Error = iota + ErrInvalidAddrRange +) + +func (err Error) Error() string { + switch err { + case ErrInvalidClockSpeed: + return "flash: invalid clock speed" + case ErrInvalidAddrRange: + return "flash: invalid address range" + default: + return "flash: unspecified error" + } +} diff --git a/flash/transport_qspi_samd.go b/flash/transport_qspi_samd.go index 5155b76a0..c61ceaa36 100644 --- a/flash/transport_qspi_samd.go +++ b/flash/transport_qspi_samd.go @@ -13,7 +13,7 @@ import ( // communicate with a serial memory chip. func NewQSPI(cs, sck, d0, d1, d2, d3 machine.Pin) *Device { return &Device{ - transport: &qspiTransport{ + trans: &qspiTransport{ cs: cs, sck: sck, d0: d0, @@ -77,8 +77,14 @@ func (q qspiTransport) supportQuadMode() bool { } func (q qspiTransport) setClockSpeed(hz uint32) error { - if divider := machine.CPUFrequency() / hz; divider < 256 { - sam.QSPI.BAUD.Reg = sam.QSPI_BAUD_BAUD_Msk & (divider << sam.QSPI_BAUD_BAUD_Pos) + // The clock speed for the QSPI peripheral is controlled by a divider, so + // we can't see the requested speed exactly. Instead we will increment the + // divider until the speed is less than or equal to the speed requested. + for div, freq := uint32(1), machine.CPUFrequency(); div < 256; div++ { + if freq/div <= hz { + sam.QSPI.BAUD.Reg = div << sam.QSPI_BAUD_BAUD_Pos + return nil + } } return ErrInvalidClockSpeed } @@ -132,9 +138,10 @@ func (q qspiTransport) readMemory(addr uint32, buf []byte) (err error) { } func (q qspiTransport) writeCommand(cmd byte, data []byte) (err error) { - iframe := uint32(sam.QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI | - sam.QSPI_INSTRFRAME_ADDRLEN_24BITS | sam.QSPI_INSTRFRAME_INSTREN | - (sam.QSPI_INSTRFRAME_TFRTYPE_WRITE << sam.QSPI_INSTRFRAME_TFRTYPE_Pos)) + iframe := uint32( + sam.QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI | + sam.QSPI_INSTRFRAME_ADDRLEN_24BITS | sam.QSPI_INSTRFRAME_INSTREN | + (sam.QSPI_INSTRFRAME_TFRTYPE_WRITE << sam.QSPI_INSTRFRAME_TFRTYPE_Pos)) if len(data) > 0 { iframe |= sam.QSPI_INSTRFRAME_DATAEN } diff --git a/flash/transport_spi.go b/flash/transport_spi.go index d10ab89d0..83facf320 100644 --- a/flash/transport_spi.go +++ b/flash/transport_spi.go @@ -18,7 +18,7 @@ type transport interface { // communicate with a serial memory chip. func NewSPI(spi *machine.SPI, mosi, miso, sck, cs machine.Pin) *Device { return &Device{ - transport: &spiTransport{ + trans: &spiTransport{ spi: spi, mosi: mosi, miso: miso, From 07bd01f43386209f79187ad1a3f4edb978c9d63c Mon Sep 17 00:00:00 2001 From: BCG Date: Sat, 15 Feb 2020 08:15:10 -0500 Subject: [PATCH 10/18] Moved instruction frames into constants --- flash/flash.go | 2 +- flash/transport_qspi_samd.go | 94 ++++++++++++++++++++++++++++-------- 2 files changed, 74 insertions(+), 22 deletions(-) diff --git a/flash/flash.go b/flash/flash.go index c2dd47c32..fb48931fc 100644 --- a/flash/flash.go +++ b/flash/flash.go @@ -139,7 +139,7 @@ func (dev *Device) Configure(config *DeviceConfig) (err error) { // Speed up to max device frequency if dev.attrs.MaxClockSpeedMHz > 0 { - //dev.trans.setClockSpeed(uint32(dev.attrs.MaxClockSpeedMHz) * 1e6) + dev.trans.setClockSpeed(uint32(dev.attrs.MaxClockSpeedMHz) * 1e6) } // Enable Quad Mode if available diff --git a/flash/transport_qspi_samd.go b/flash/transport_qspi_samd.go index c61ceaa36..b812953b5 100644 --- a/flash/transport_qspi_samd.go +++ b/flash/transport_qspi_samd.go @@ -31,6 +31,49 @@ const ( // High address of the QSPI address space on SAMD51 qspi_AHB_HI = 0x05000000 + + iframeRunCommand = 0x0 | + sam.QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI | + sam.QSPI_INSTRFRAME_ADDRLEN_24BITS | + sam.QSPI_INSTRFRAME_INSTREN | + (sam.QSPI_INSTRFRAME_TFRTYPE_READ << sam.QSPI_INSTRFRAME_TFRTYPE_Pos) + + iframeReadCommand = 0x0 | + sam.QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI | + sam.QSPI_INSTRFRAME_ADDRLEN_24BITS | + sam.QSPI_INSTRFRAME_INSTREN | + sam.QSPI_INSTRFRAME_DATAEN | + (sam.QSPI_INSTRFRAME_TFRTYPE_READ << sam.QSPI_INSTRFRAME_TFRTYPE_Pos) + + iframeReadMemory = 0x0 | + sam.QSPI_INSTRFRAME_WIDTH_QUAD_OUTPUT | + sam.QSPI_INSTRFRAME_ADDRLEN_24BITS | + sam.QSPI_INSTRFRAME_INSTREN | + sam.QSPI_INSTRFRAME_DATAEN | + sam.QSPI_INSTRFRAME_ADDREN | + (8 << sam.QSPI_INSTRFRAME_DUMMYLEN_Pos) | + (sam.QSPI_INSTRFRAME_TFRTYPE_READMEMORY << sam.QSPI_INSTRFRAME_TFRTYPE_Pos) + + iframeWriteCommand = 0x0 | + sam.QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI | + sam.QSPI_INSTRFRAME_ADDRLEN_24BITS | + sam.QSPI_INSTRFRAME_INSTREN | + (sam.QSPI_INSTRFRAME_TFRTYPE_WRITE << sam.QSPI_INSTRFRAME_TFRTYPE_Pos) + + iframeEraseCommand = 0x0 | + sam.QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI | + sam.QSPI_INSTRFRAME_ADDRLEN_24BITS | + sam.QSPI_INSTRFRAME_INSTREN | + sam.QSPI_INSTRFRAME_ADDREN | + (sam.QSPI_INSTRFRAME_TFRTYPE_WRITE << sam.QSPI_INSTRFRAME_TFRTYPE_Pos) + + iframeWriteMemory = 0x0 | + sam.QSPI_INSTRFRAME_WIDTH_QUAD_OUTPUT | + sam.QSPI_INSTRFRAME_ADDRLEN_24BITS | + sam.QSPI_INSTRFRAME_INSTREN | + sam.QSPI_INSTRFRAME_ADDREN | + sam.QSPI_INSTRFRAME_DATAEN | + (sam.QSPI_INSTRFRAME_TFRTYPE_WRITEMEMORY << sam.QSPI_INSTRFRAME_TFRTYPE_Pos) ) type qspiTransport struct { @@ -91,9 +134,7 @@ func (q qspiTransport) setClockSpeed(hz uint32) error { func (q qspiTransport) runCommand(cmd byte) (err error) { sam.QSPI.INSTRCTRL.Set(uint32(cmd)) - sam.QSPI.INSTRFRAME.Set(sam.QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI | - sam.QSPI_INSTRFRAME_ADDRLEN_24BITS | sam.QSPI_INSTRFRAME_INSTREN | - (sam.QSPI_INSTRFRAME_TFRTYPE_READ << sam.QSPI_INSTRFRAME_TFRTYPE_Pos)) + sam.QSPI.INSTRFRAME.Set(iframeRunCommand) sam.QSPI.INSTRFRAME.Get() // dummy read for synchronization, as per datasheet q.endTransfer() return @@ -102,10 +143,7 @@ func (q qspiTransport) runCommand(cmd byte) (err error) { func (q qspiTransport) readCommand(cmd byte, buf []byte) (err error) { q.disableAndClearCache() sam.QSPI.INSTRCTRL.Set(uint32(cmd)) - const iframe = sam.QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI | sam.QSPI_INSTRFRAME_DATAEN | - sam.QSPI_INSTRFRAME_ADDRLEN_24BITS | sam.QSPI_INSTRFRAME_INSTREN | - (sam.QSPI_INSTRFRAME_TFRTYPE_READ << sam.QSPI_INSTRFRAME_TFRTYPE_Pos) - sam.QSPI.INSTRFRAME.Set(iframe) + sam.QSPI.INSTRFRAME.Set(iframeReadCommand) sam.QSPI.INSTRFRAME.Get() // dummy read for synchronization, as per datasheet var ptr uintptr = qspi_AHB_LO for i := range buf { @@ -123,11 +161,7 @@ func (q qspiTransport) readMemory(addr uint32, buf []byte) (err error) { } q.disableAndClearCache() sam.QSPI.INSTRCTRL.Set(uint32(cmdQuadRead)) - const iframe = sam.QSPI_INSTRFRAME_WIDTH_QUAD_OUTPUT | sam.QSPI_INSTRFRAME_DATAEN | - sam.QSPI_INSTRFRAME_ADDRLEN_24BITS | sam.QSPI_INSTRFRAME_INSTREN | - sam.QSPI_INSTRFRAME_ADDREN | (8 << sam.QSPI_INSTRFRAME_DUMMYLEN_Pos) | - (sam.QSPI_INSTRFRAME_TFRTYPE_READMEMORY << sam.QSPI_INSTRFRAME_TFRTYPE_Pos) - sam.QSPI.INSTRFRAME.Set(iframe) + sam.QSPI.INSTRFRAME.Set(iframeReadMemory) sam.QSPI.INSTRFRAME.Get() // dummy read for synchronization, as per datasheet ln := len(buf) sl := (*[1 << 28]byte)(unsafe.Pointer(uintptr(qspi_AHB_LO + addr)))[:ln:ln] @@ -138,16 +172,13 @@ func (q qspiTransport) readMemory(addr uint32, buf []byte) (err error) { } func (q qspiTransport) writeCommand(cmd byte, data []byte) (err error) { - iframe := uint32( - sam.QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI | - sam.QSPI_INSTRFRAME_ADDRLEN_24BITS | sam.QSPI_INSTRFRAME_INSTREN | - (sam.QSPI_INSTRFRAME_TFRTYPE_WRITE << sam.QSPI_INSTRFRAME_TFRTYPE_Pos)) + var dataen uint32 if len(data) > 0 { - iframe |= sam.QSPI_INSTRFRAME_DATAEN + dataen = sam.QSPI_INSTRFRAME_DATAEN } q.disableAndClearCache() sam.QSPI.INSTRCTRL.Set(uint32(cmd)) - sam.QSPI.INSTRFRAME.Set(iframe) + sam.QSPI.INSTRFRAME.Set(iframeWriteCommand | dataen) sam.QSPI.INSTRFRAME.Get() // dummy read for synchronization, as per datasheet var ptr uintptr = qspi_AHB_LO for i := range data { @@ -159,12 +190,33 @@ func (q qspiTransport) writeCommand(cmd byte, data []byte) (err error) { return } -func (q qspiTransport) eraseCommand(cmd byte, address uint32) (err error) { - panic("implement me") +func (q qspiTransport) eraseCommand(cmd byte, addr uint32) (err error) { + q.disableAndClearCache() + sam.QSPI.INSTRADDR.Set(addr) + sam.QSPI.INSTRCTRL.Set(uint32(cmd)) + sam.QSPI.INSTRFRAME.Set(iframeEraseCommand) + sam.QSPI.INSTRFRAME.Get() // dummy read for synchronization, as per datasheet + q.endTransfer() + q.enableCache() + return } func (q qspiTransport) writeMemory(addr uint32, data []byte) (err error) { - panic("implement me") + if (addr + uint32(len(data))) > (qspi_AHB_HI - qspi_AHB_LO) { + return ErrInvalidAddrRange + } + q.disableAndClearCache() + sam.QSPI.INSTRCTRL.Set(uint32(cmdQuadRead)) + sam.QSPI.INSTRFRAME.Set(iframeWriteMemory) + sam.QSPI.INSTRFRAME.Get() // dummy read for synchronization, as per datasheet + var ptr = qspi_AHB_LO + uintptr(addr) + for i := range data { + volatile.StoreUint8((*uint8)(unsafe.Pointer(ptr)), data[i]) + ptr++ + } + q.endTransfer() + q.enableCache() + return } func (q qspiTransport) enableCache() { From 1090fb0afcc7634b3c63b3b9eb696a3a4ce094b0 Mon Sep 17 00:00:00 2001 From: BCG Date: Sat, 15 Feb 2020 09:10:18 -0500 Subject: [PATCH 11/18] Cleaning up SAMD QSPI implementation --- flash/transport_qspi_samd.go | 107 +++++++++++++++++++---------------- 1 file changed, 58 insertions(+), 49 deletions(-) diff --git a/flash/transport_qspi_samd.go b/flash/transport_qspi_samd.go index b812953b5..5a1dca051 100644 --- a/flash/transport_qspi_samd.go +++ b/flash/transport_qspi_samd.go @@ -32,12 +32,14 @@ const ( // High address of the QSPI address space on SAMD51 qspi_AHB_HI = 0x05000000 + // Instruction frame for running sending a command to the device iframeRunCommand = 0x0 | sam.QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI | sam.QSPI_INSTRFRAME_ADDRLEN_24BITS | sam.QSPI_INSTRFRAME_INSTREN | (sam.QSPI_INSTRFRAME_TFRTYPE_READ << sam.QSPI_INSTRFRAME_TFRTYPE_Pos) + // Instruction frame for running a command that returns data iframeReadCommand = 0x0 | sam.QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI | sam.QSPI_INSTRFRAME_ADDRLEN_24BITS | @@ -45,6 +47,7 @@ const ( sam.QSPI_INSTRFRAME_DATAEN | (sam.QSPI_INSTRFRAME_TFRTYPE_READ << sam.QSPI_INSTRFRAME_TFRTYPE_Pos) + // Instruction frame to set up the device to read from memory iframeReadMemory = 0x0 | sam.QSPI_INSTRFRAME_WIDTH_QUAD_OUTPUT | sam.QSPI_INSTRFRAME_ADDRLEN_24BITS | @@ -54,19 +57,14 @@ const ( (8 << sam.QSPI_INSTRFRAME_DUMMYLEN_Pos) | (sam.QSPI_INSTRFRAME_TFRTYPE_READMEMORY << sam.QSPI_INSTRFRAME_TFRTYPE_Pos) + // Instruction frame for running a command that requires parameter data iframeWriteCommand = 0x0 | sam.QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI | sam.QSPI_INSTRFRAME_ADDRLEN_24BITS | sam.QSPI_INSTRFRAME_INSTREN | (sam.QSPI_INSTRFRAME_TFRTYPE_WRITE << sam.QSPI_INSTRFRAME_TFRTYPE_Pos) - iframeEraseCommand = 0x0 | - sam.QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI | - sam.QSPI_INSTRFRAME_ADDRLEN_24BITS | - sam.QSPI_INSTRFRAME_INSTREN | - sam.QSPI_INSTRFRAME_ADDREN | - (sam.QSPI_INSTRFRAME_TFRTYPE_WRITE << sam.QSPI_INSTRFRAME_TFRTYPE_Pos) - + // Instruction frame to set up the device for writing to memory iframeWriteMemory = 0x0 | sam.QSPI_INSTRFRAME_WIDTH_QUAD_OUTPUT | sam.QSPI_INSTRFRAME_ADDRLEN_24BITS | @@ -74,6 +72,14 @@ const ( sam.QSPI_INSTRFRAME_ADDREN | sam.QSPI_INSTRFRAME_DATAEN | (sam.QSPI_INSTRFRAME_TFRTYPE_WRITEMEMORY << sam.QSPI_INSTRFRAME_TFRTYPE_Pos) + + // Instruction frame for running an erase command that requires and address + iframeEraseCommand = 0x0 | + sam.QSPI_INSTRFRAME_WIDTH_SINGLE_BIT_SPI | + sam.QSPI_INSTRFRAME_ADDRLEN_24BITS | + sam.QSPI_INSTRFRAME_INSTREN | + sam.QSPI_INSTRFRAME_ADDREN | + (sam.QSPI_INSTRFRAME_TFRTYPE_WRITE << sam.QSPI_INSTRFRAME_TFRTYPE_Pos) ) type qspiTransport struct { @@ -133,23 +139,15 @@ func (q qspiTransport) setClockSpeed(hz uint32) error { } func (q qspiTransport) runCommand(cmd byte) (err error) { - sam.QSPI.INSTRCTRL.Set(uint32(cmd)) - sam.QSPI.INSTRFRAME.Set(iframeRunCommand) - sam.QSPI.INSTRFRAME.Get() // dummy read for synchronization, as per datasheet + q.runInstruction(cmd, iframeRunCommand) q.endTransfer() return } func (q qspiTransport) readCommand(cmd byte, buf []byte) (err error) { q.disableAndClearCache() - sam.QSPI.INSTRCTRL.Set(uint32(cmd)) - sam.QSPI.INSTRFRAME.Set(iframeReadCommand) - sam.QSPI.INSTRFRAME.Get() // dummy read for synchronization, as per datasheet - var ptr uintptr = qspi_AHB_LO - for i := range buf { - buf[i] = volatile.LoadUint8((*uint8)(unsafe.Pointer(ptr))) - ptr++ - } + q.runInstruction(cmd, iframeReadCommand) + q.readInto(buf, 0) q.endTransfer() q.enableCache() return @@ -160,12 +158,8 @@ func (q qspiTransport) readMemory(addr uint32, buf []byte) (err error) { return ErrInvalidAddrRange } q.disableAndClearCache() - sam.QSPI.INSTRCTRL.Set(uint32(cmdQuadRead)) - sam.QSPI.INSTRFRAME.Set(iframeReadMemory) - sam.QSPI.INSTRFRAME.Get() // dummy read for synchronization, as per datasheet - ln := len(buf) - sl := (*[1 << 28]byte)(unsafe.Pointer(uintptr(qspi_AHB_LO + addr)))[:ln:ln] - copy(buf, sl) + q.runInstruction(cmdQuadRead, iframeReadMemory) + q.readInto(buf, addr) q.endTransfer() q.enableCache() return @@ -177,48 +171,40 @@ func (q qspiTransport) writeCommand(cmd byte, data []byte) (err error) { dataen = sam.QSPI_INSTRFRAME_DATAEN } q.disableAndClearCache() - sam.QSPI.INSTRCTRL.Set(uint32(cmd)) - sam.QSPI.INSTRFRAME.Set(iframeWriteCommand | dataen) - sam.QSPI.INSTRFRAME.Get() // dummy read for synchronization, as per datasheet - var ptr uintptr = qspi_AHB_LO - for i := range data { - volatile.StoreUint8((*uint8)(unsafe.Pointer(ptr)), data[i]) - ptr++ - } + q.runInstruction(cmd, iframeWriteCommand|dataen) + q.writeFrom(data, 0) q.endTransfer() q.enableCache() return } -func (q qspiTransport) eraseCommand(cmd byte, addr uint32) (err error) { +func (q qspiTransport) writeMemory(addr uint32, data []byte) (err error) { + if (addr + uint32(len(data))) > (qspi_AHB_HI - qspi_AHB_LO) { + return ErrInvalidAddrRange + } q.disableAndClearCache() - sam.QSPI.INSTRADDR.Set(addr) - sam.QSPI.INSTRCTRL.Set(uint32(cmd)) - sam.QSPI.INSTRFRAME.Set(iframeEraseCommand) - sam.QSPI.INSTRFRAME.Get() // dummy read for synchronization, as per datasheet + q.runInstruction(cmdQuadPageProgram, iframeWriteMemory) + q.writeFrom(data, addr) q.endTransfer() q.enableCache() return } -func (q qspiTransport) writeMemory(addr uint32, data []byte) (err error) { - if (addr + uint32(len(data))) > (qspi_AHB_HI - qspi_AHB_LO) { - return ErrInvalidAddrRange - } +func (q qspiTransport) eraseCommand(cmd byte, addr uint32) (err error) { q.disableAndClearCache() - sam.QSPI.INSTRCTRL.Set(uint32(cmdQuadRead)) - sam.QSPI.INSTRFRAME.Set(iframeWriteMemory) - sam.QSPI.INSTRFRAME.Get() // dummy read for synchronization, as per datasheet - var ptr = qspi_AHB_LO + uintptr(addr) - for i := range data { - volatile.StoreUint8((*uint8)(unsafe.Pointer(ptr)), data[i]) - ptr++ - } + sam.QSPI.INSTRADDR.Set(addr) + q.runInstruction(cmd, iframeEraseCommand) q.endTransfer() q.enableCache() return } +func (q qspiTransport) runInstruction(cmd byte, iframe uint32) { + sam.QSPI.INSTRCTRL.Set(uint32(cmd)) + sam.QSPI.INSTRFRAME.Set(iframe) + sam.QSPI.INSTRFRAME.Get() // dummy read for synchronization, as per datasheet +} + func (q qspiTransport) enableCache() { sam.CMCC.CTRL.SetBits(sam.CMCC_CTRL_CEN) } @@ -236,3 +222,26 @@ func (q qspiTransport) endTransfer() { } sam.QSPI.INTFLAG.Set(sam.QSPI_INTFLAG_INSTREND) } + +func (q qspiTransport) readInto(buf []byte, addr uint32) { + var ptr = qspi_AHB_LO + uintptr(addr) + for i := range buf { + buf[i] = volatile.LoadUint8((*uint8)(unsafe.Pointer(ptr))) + ptr++ + } + /* // NB: for some reason this reads data that results from commands in + // a different endianess than the loop above, but works fine for reading + // from memory. The above loop seems to work fine in both cases oddly + ln := len(buf) + sl := (*[1 << 28]byte)(unsafe.Pointer(uintptr(qspi_AHB_LO + addr)))[:ln:ln] + copy(buf, sl) + */ +} + +func (q qspiTransport) writeFrom(buf []byte, addr uint32) { + var ptr = qspi_AHB_LO + uintptr(addr) + for i := range buf { + volatile.StoreUint8((*uint8)(unsafe.Pointer(ptr)), buf[i]) + ptr++ + } +} From 2bf2593138a55601ac51723c46c41f7a63d83ebb Mon Sep 17 00:00:00 2001 From: BCG Date: Sat, 22 Feb 2020 14:16:27 -0500 Subject: [PATCH 12/18] Added comments and switched register operations to use volatile package --- flash/flash.go | 15 ++++++++++++++- flash/transport_qspi_samd.go | 14 +++++++------- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/flash/flash.go b/flash/flash.go index fb48931fc..30ae0836d 100644 --- a/flash/flash.go +++ b/flash/flash.go @@ -53,9 +53,16 @@ func (sn SerialNumber) String() string { return fmt.Sprintf("%8X", uint64(sn)) } +// Attrs represent the differences in hardware characteristics and capabilities +// of various SPI flash memory devices. type Attrs struct { + + // TotalSize is the number of bytes that the flash device can store TotalSize uint32 - StartUp time.Duration + + // StartUp is the duration of time between when the device is reset and when + // it is ready to operation + StartUp time.Duration // Three response bytes to 0x9f JEDEC ID command. JedecID @@ -89,6 +96,10 @@ type Attrs struct { SingleStatusByte bool } +// Configure sets up the device and the underlying transport mechanism. The +// DeviceConfig argument allows the caller to specify an instance of the +// DeviceIdentifier interface that, if provided, will be used to retrieve the +// attributes of the device based on the JEDEC ID. func (dev *Device) Configure(config *DeviceConfig) (err error) { dev.trans.configure(config) @@ -305,6 +316,8 @@ func (dev *Device) ReadStatus2() (status byte, err error) { return buf[0], err } +// WaitUntilReady queries the status register until the device is ready for the +// next operation. func (dev *Device) WaitUntilReady() error { expire := time.Now().UnixNano() + int64(1*time.Second) for s, err := dev.ReadStatus(); (s & 0x03) > 0; s, err = dev.ReadStatus() { diff --git a/flash/transport_qspi_samd.go b/flash/transport_qspi_samd.go index 5a1dca051..63ce00918 100644 --- a/flash/transport_qspi_samd.go +++ b/flash/transport_qspi_samd.go @@ -113,9 +113,9 @@ func (q qspiTransport) configure(config *DeviceConfig) { _ = q.setClockSpeed(4e6) // configure the CTRLB register - sam.QSPI.CTRLB.Reg = sam.QSPI_CTRLB_MODE_MEMORY | + sam.QSPI.CTRLB.Set(sam.QSPI_CTRLB_MODE_MEMORY | (sam.QSPI_CTRLB_DATALEN_8BITS << sam.QSPI_CTRLB_DATALEN_Pos) | - (sam.QSPI_CTRLB_CSMODE_LASTXFER << sam.QSPI_CTRLB_CSMODE_Pos) + (sam.QSPI_CTRLB_CSMODE_LASTXFER << sam.QSPI_CTRLB_CSMODE_Pos)) // enable the peripheral sam.QSPI.CTRLA.SetBits(sam.QSPI_CTRLA_ENABLE) @@ -127,11 +127,11 @@ func (q qspiTransport) supportQuadMode() bool { func (q qspiTransport) setClockSpeed(hz uint32) error { // The clock speed for the QSPI peripheral is controlled by a divider, so - // we can't see the requested speed exactly. Instead we will increment the + // we can't set the requested speed exactly. Instead we will increment the // divider until the speed is less than or equal to the speed requested. for div, freq := uint32(1), machine.CPUFrequency(); div < 256; div++ { if freq/div <= hz { - sam.QSPI.BAUD.Reg = div << sam.QSPI_BAUD_BAUD_Pos + sam.QSPI.BAUD.Set(div << sam.QSPI_BAUD_BAUD_Pos) return nil } } @@ -229,9 +229,9 @@ func (q qspiTransport) readInto(buf []byte, addr uint32) { buf[i] = volatile.LoadUint8((*uint8)(unsafe.Pointer(ptr))) ptr++ } - /* // NB: for some reason this reads data that results from commands in - // a different endianess than the loop above, but works fine for reading - // from memory. The above loop seems to work fine in both cases oddly + /* // NB(bcg): for some reason this reads data that results from commands in + // a different byte order than the loop above, but works fine for reading + // from memory. Oddly, the above loop seems to work fine in both cases. ln := len(buf) sl := (*[1 << 28]byte)(unsafe.Pointer(uintptr(qspi_AHB_LO + addr)))[:ln:ln] copy(buf, sl) From 608f0e38ce129907230ccb7d8b0c59ed8f5088d4 Mon Sep 17 00:00:00 2001 From: BCG Date: Thu, 26 Mar 2020 00:18:38 -0400 Subject: [PATCH 13/18] Named parameter more accurately --- flash/flash.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flash/flash.go b/flash/flash.go index 30ae0836d..ff2a05042 100644 --- a/flash/flash.go +++ b/flash/flash.go @@ -270,14 +270,14 @@ func (dev *Device) WriteEnable() error { } // EraseBlock erases a block of memory at the specified address -func (dev *Device) EraseBlock(addr uint32) error { +func (dev *Device) EraseBlock(blockNumber uint32) error { if err := dev.WaitUntilReady(); err != nil { return err } if err := dev.WriteEnable(); err != nil { return err } - return dev.trans.eraseCommand(cmdEraseBlock, addr*BlockSize) + return dev.trans.eraseCommand(cmdEraseBlock, blockNumber*BlockSize) } // EraseSector erases a sector of memory at the specified address From 3c0b7633b0d93e9420e7b68b1cafce53adc048c0 Mon Sep 17 00:00:00 2001 From: BCG Date: Fri, 27 Mar 2020 08:46:17 -0400 Subject: [PATCH 14/18] Updated flash device driver to satisfy io.ReaderAt and io.WriterAt interfaces --- flash/flash.go | 95 +++++++++++++++++++++++++++++------------- flash/transport_spi.go | 5 +++ 2 files changed, 70 insertions(+), 30 deletions(-) diff --git a/flash/flash.go b/flash/flash.go index ff2a05042..2fdee7b46 100644 --- a/flash/flash.go +++ b/flash/flash.go @@ -1,7 +1,7 @@ package flash import ( - "fmt" + "errors" "time" ) @@ -40,19 +40,9 @@ func (id JedecID) Uint32() uint32 { return uint32(id.ManufID)<<16 | uint32(id.MemType)<<8 | uint32(id.Capacity) } -// String implements the Stringer interface to print out the hex value of the ID -func (id JedecID) String() string { - return fmt.Sprintf("%06X", id.Uint32()) -} - // SerialNumber represents a serial number read from a flash memory device type SerialNumber uint64 -// String implements the Stringer interface to print out the hex value of the SN -func (sn SerialNumber) String() string { - return fmt.Sprintf("%8X", uint64(sn)) -} - // Attrs represent the differences in hardware characteristics and capabilities // of various SPI flash memory devices. type Attrs struct { @@ -219,6 +209,7 @@ func (dev *Device) ReadJEDEC() (JedecID, error) { } // ReadSerialNumber reads the serial numbers from the connected device. +// TODO: maybe check if byte order / endianess is correct, probably is not func (dev *Device) ReadSerialNumber() (SerialNumber, error) { sn := make([]byte, 12) if err := dev.trans.readCommand(0x4B, sn); err != nil { @@ -229,20 +220,35 @@ func (dev *Device) ReadSerialNumber() (SerialNumber, error) { uint64(sn[6])<<0x28 | uint64(sn[5])<<0x30 | uint64(sn[4])<<0x38), nil } -// ReadBuffer fills the provided buffer with memory read from the device -// starting at the provided address. -func (dev *Device) ReadBuffer(addr uint32, buf []byte) error { +// Size returns the size of this memory, in bytes. +func (dev *Device) Size() int64 { + if dev.attrs.TotalSize < 1 { + // in case a DeviceIdentifier function wasn't used, use the capacity + // specified in the JEDEC ID instead + return int64(dev.attrs.Capacity) + } + return int64(dev.attrs.TotalSize) +} + +// ReadAt satisfies the io.ReaderAt interface, and fills the provided buffer +// with memory read from the device starting at the provided address. +func (dev *Device) ReadAt(buf []byte, addr int64) (int, error) { if err := dev.WaitUntilReady(); err != nil { - return err + return 0, err } - return dev.trans.readMemory(addr, buf) + if err := dev.trans.readMemory(uint32(addr), buf); err != nil { + return 0, err + } + return len(buf), nil } -// WriteBuffer writes data to the device, one page at a time, starting at the -// provided address. This method assumes that the destination is already erased. -func (dev *Device) WriteBuffer(addr uint32, buf []byte) (n int, err error) { +// WriteAt satisfies the io.WriterAt interface and writes data to the device, +// one page at a time, starting at the provided address. This method assumes +// that the destination is already erased. +func (dev *Device) WriteAt(buf []byte, addr int64) (n int, err error) { remain := uint32(len(buf)) idx := uint32(0) + loc := uint32(addr) for remain > 0 { if err = dev.WaitUntilReady(); err != nil { return @@ -250,26 +256,55 @@ func (dev *Device) WriteBuffer(addr uint32, buf []byte) (n int, err error) { if err = dev.WriteEnable(); err != nil { return } - leftOnPage := PageSize - (addr & (PageSize - 1)) + leftOnPage := PageSize - (loc & (PageSize - 1)) toWrite := remain if leftOnPage < remain { toWrite = leftOnPage } - if err = dev.trans.writeMemory(addr, buf[idx:idx+toWrite]); err != nil { + if err = dev.trans.writeMemory(loc, buf[idx:idx+toWrite]); err != nil { return } idx += toWrite - addr += toWrite + loc += toWrite remain -= toWrite } return len(buf) - int(remain), nil } +// WriteBlockSize returns the block size in which data can be written to +// memory. It can be used by a client to optimize writes, non-aligned writes +// should always work correctly. +// For SPI NOR flash this is the page size, usually/always 256. +func (dev *Device) WriteBlockSize() int64 { + return PageSize +} + +// EraseBlockSize returns the smallest erasable area on this particular chip +// in bytes. This is used for the block size in EraseBlocks. +// For SPI NOR flash this is the sector size, usually/always 4096. +func (dev *Device) EraseBlockSize() int64 { + return SectorSize +} + +// EraseBlocks erases the given number of blocks. An implementation may +// transparently coalesce ranges of blocks into larger bundles if the chip +// supports this. The start and len parameters are in block numbers, use +// EraseBlockSize to map addresses to blocks. +func (dev *Device) EraseBlocks(start, len int64) error { + // TODO: maybe combine sector erase operations into block erase operations + for i := start; i < start+len; i++ { + if err := dev.EraseSector(uint32(i)); err != nil { + return err + } + } + return nil +} + func (dev *Device) WriteEnable() error { return dev.trans.runCommand(cmdWriteEnable) } -// EraseBlock erases a block of memory at the specified address +// EraseBlock erases a block of memory at the specified index func (dev *Device) EraseBlock(blockNumber uint32) error { if err := dev.WaitUntilReady(); err != nil { return err @@ -280,19 +315,19 @@ func (dev *Device) EraseBlock(blockNumber uint32) error { return dev.trans.eraseCommand(cmdEraseBlock, blockNumber*BlockSize) } -// EraseSector erases a sector of memory at the specified address -func (dev *Device) EraseSector(addr uint32) error { +// EraseSector erases a sector of memory at the given index +func (dev *Device) EraseSector(sectorNumber uint32) error { if err := dev.WaitUntilReady(); err != nil { return err } if err := dev.WriteEnable(); err != nil { return err } - return dev.trans.eraseCommand(cmdEraseSector, addr*SectorSize) + return dev.trans.eraseCommand(cmdEraseSector, sectorNumber*SectorSize) } // EraseChip erases the entire flash memory chip -func (dev *Device) EraseChip() error { +func (dev *Device) EraseAll() error { if err := dev.WaitUntilReady(); err != nil { return err } @@ -324,9 +359,9 @@ func (dev *Device) WaitUntilReady() error { if err != nil { return err } - } - if time.Now().UnixNano() > expire { - return fmt.Errorf("WaitUntilReady expired") + if time.Now().UnixNano() > expire { + return errors.New("WaitUntilReady expired") + } } return nil } diff --git a/flash/transport_spi.go b/flash/transport_spi.go index 83facf320..2da3d5550 100644 --- a/flash/transport_spi.go +++ b/flash/transport_spi.go @@ -46,6 +46,11 @@ func (tr *spiTransport) configure(config *DeviceConfig) { } func (tr *spiTransport) setClockSpeed(hz uint32) error { + // TODO: un-hardcode this max speed; it is probably a sensible + // default maximum for atsamd and nrf at least + if hz > 24*1e6 { + hz = 24 * 1e6 + } tr.spi.Configure(machine.SPIConfig{ Frequency: hz, MISO: tr.miso, From 20e92fc4227f01fcd25b3d84a1831628c33d4d35 Mon Sep 17 00:00:00 2001 From: BCG Date: Sat, 28 Mar 2020 02:45:21 -0400 Subject: [PATCH 15/18] Small tweaks for startup delay and error value --- flash/flash.go | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/flash/flash.go b/flash/flash.go index 2fdee7b46..1fc9c32f0 100644 --- a/flash/flash.go +++ b/flash/flash.go @@ -1,7 +1,6 @@ package flash import ( - "errors" "time" ) @@ -131,16 +130,16 @@ func (dev *Device) Configure(config *DeviceConfig) (err error) { } // Wait for the reset - 30us by default - dur := int64(dev.attrs.StartUp) - if dur == 0 { - dur = int64(30 * time.Microsecond) - } - for stop := time.Now().UnixNano() + dur; stop > time.Now().UnixNano(); { + const delay = int64(30 * time.Microsecond) + for stop := time.Now().UnixNano() + delay; stop > time.Now().UnixNano(); { } // Speed up to max device frequency if dev.attrs.MaxClockSpeedMHz > 0 { - dev.trans.setClockSpeed(uint32(dev.attrs.MaxClockSpeedMHz) * 1e6) + err := dev.trans.setClockSpeed(uint32(dev.attrs.MaxClockSpeedMHz) * 1e6) + if err != nil { + return err + } } // Enable Quad Mode if available @@ -360,7 +359,7 @@ func (dev *Device) WaitUntilReady() error { return err } if time.Now().UnixNano() > expire { - return errors.New("WaitUntilReady expired") + return ErrWaitExpired } } return nil @@ -391,6 +390,7 @@ const ( _ = iota ErrInvalidClockSpeed Error = iota ErrInvalidAddrRange + ErrWaitExpired ) func (err Error) Error() string { @@ -399,6 +399,8 @@ func (err Error) Error() string { return "flash: invalid clock speed" case ErrInvalidAddrRange: return "flash: invalid address range" + case ErrWaitExpired: + return "flash: wait until ready expired" default: return "flash: unspecified error" } From da5aad1083880f4f6d10a35340b1d35f6496757c Mon Sep 17 00:00:00 2001 From: BCG Date: Mon, 30 Mar 2020 22:47:54 -0400 Subject: [PATCH 16/18] Added smoke tests --- Makefile | 4 ++++ examples/flash/console/console_example.go | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index ee46143ff..80cccd562 100644 --- a/Makefile +++ b/Makefile @@ -39,6 +39,10 @@ smoke-test: @md5sum ./build/test.hex tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/espat/espstation/main.go @md5sum ./build/test.hex + tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/flash/console/spi + @md5sum ./build/test.hex + tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/flash/console/qspi + @md5sum ./build/test.hex tinygo build -size short -o ./build/test.hex -target=feather-m0 ./examples/gps/i2c/main.go @md5sum ./build/test.hex tinygo build -size short -o ./build/test.hex -target=feather-m0 ./examples/gps/uart/main.go diff --git a/examples/flash/console/console_example.go b/examples/flash/console/console_example.go index 8840ee5f9..ee2f50f86 100644 --- a/examples/flash/console/console_example.go +++ b/examples/flash/console/console_example.go @@ -177,7 +177,7 @@ func erase(argv []string) { println("Sector erase error: " + err.Error() + "\r\n") } } else if argv[1] == "chip" { - if err = dev.EraseChip(); err != nil { + if err = dev.EraseAll(); err != nil { println("Chip erase error: " + err.Error() + "\r\n") } } else { @@ -196,7 +196,7 @@ func write(argv []string) { return } buf := []byte(argv[2]) - if _, err = dev.WriteBuffer(uint32(addr), buf); err != nil { + if _, err = dev.WriteAt(buf, int64(addr)); err != nil { println("Write error: " + err.Error() + "\r\n") } } @@ -229,7 +229,7 @@ func xxd(argv []string) { return } buf := store[0:size] - dev.ReadBuffer(uint32(addr), buf) + dev.ReadAt(buf, int64(addr)) xxdfprint(os.Stdout, uint32(addr), buf) } From a1ce77765351f6c351799804b253f3563f91fdda Mon Sep 17 00:00:00 2001 From: BCG Date: Mon, 30 Mar 2020 23:09:22 -0400 Subject: [PATCH 17/18] Added SPI flash to README --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a55596566..6b262b812 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ func main() { ## Currently supported devices -The following 45 devices are supported. +The following 46 devices are supported. | Device Name | Interface Type | |----------|-------------| @@ -90,6 +90,7 @@ The following 45 devices are supported. | [Shift register (PISO)](https://en.wikipedia.org/wiki/Shift_register#Parallel-in_serial-out_\(PISO\)) | GPIO | | [Shift registers (SIPO)](https://en.wikipedia.org/wiki/Shift_register#Serial-in_parallel-out_(SIPO)) | GPIO | | [SHT3x Digital Humidity Sensor](https://www.sensirion.com/fileadmin/user_upload/customers/sensirion/Dokumente/0_Datasheets/Humidity/Sensirion_Humidity_Sensors_SHT3x_Datasheet_digital.pdf) | I2C | +| [SPI NOR Flash Memory](https://en.wikipedia.org/wiki/Flash_memory#NOR_flash) | SPI/QSPI | | [SSD1306 OLED display](https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf) | I2C / SPI | | [SSD1331 TFT color display](https://www.crystalfontz.com/controllers/SolomonSystech/SSD1331/381/) | SPI | | [ST7735 TFT color display](https://www.crystalfontz.com/controllers/Sitronix/ST7735R/319/) | SPI | From b7a2ce9afc3d6c1043b687c744c27af9cceb6561 Mon Sep 17 00:00:00 2001 From: BCG Date: Mon, 30 Mar 2020 23:28:06 -0400 Subject: [PATCH 18/18] switched busy loop to time.Sleep --- flash/flash.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/flash/flash.go b/flash/flash.go index 1fc9c32f0..aa26bc6c8 100644 --- a/flash/flash.go +++ b/flash/flash.go @@ -130,9 +130,7 @@ func (dev *Device) Configure(config *DeviceConfig) (err error) { } // Wait for the reset - 30us by default - const delay = int64(30 * time.Microsecond) - for stop := time.Now().UnixNano() + delay; stop > time.Now().UnixNano(); { - } + time.Sleep(30 * time.Microsecond) // Speed up to max device frequency if dev.attrs.MaxClockSpeedMHz > 0 {