diff --git a/.gitignore b/.gitignore index 7c2f12b312..5efb185980 100644 --- a/.gitignore +++ b/.gitignore @@ -35,5 +35,9 @@ test.exe test.gba test.hex test.nro +test.uf2 test.wasm wasm.wasm + +*.uf2 +*.elf \ No newline at end of file diff --git a/builder/build.go b/builder/build.go index 780dc8df49..6b6ef04631 100644 --- a/builder/build.go +++ b/builder/build.go @@ -25,6 +25,7 @@ import ( "strings" "github.com/gofrs/flock" + "github.com/soypat/tinyboot/boot/picobin" "github.com/tinygo-org/tinygo/compileopts" "github.com/tinygo-org/tinygo/compiler" "github.com/tinygo-org/tinygo/goenv" @@ -804,6 +805,12 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe return fmt.Errorf("could not modify stack sizes: %w", err) } } + + // Apply patches of bootloader in the order they appear. + if len(config.Target.BootPatches) > 0 { + err = applyPatches(result.Executable, config.Target.BootPatches) + } + if config.RP2040BootPatch() { // Patch the second stage bootloader CRC into the .boot2 section err = patchRP2040BootCRC(result.Executable) @@ -1422,6 +1429,23 @@ func printStacks(calculatedStacks []string, stackSizes map[string]functionStackS } } +func applyPatches(executable string, bootPatches []string) (err error) { + for _, patch := range bootPatches { + switch patch { + case "rp2040": + err = patchRP2040BootCRC(executable) + case "rp2350": + err = patchRP2350BootIMAGE_DEF(executable) + default: + err = errors.New("undefined boot patch name") + } + if err != nil { + return fmt.Errorf("apply boot patch %q: %w", patch, err) + } + } + return nil +} + // RP2040 second stage bootloader CRC32 calculation // // Spec: https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf @@ -1433,7 +1457,7 @@ func patchRP2040BootCRC(executable string) error { } if len(bytes) != 256 { - return fmt.Errorf("rp2040 .boot2 section must be exactly 256 bytes") + return fmt.Errorf("rp2040 .boot2 section must be exactly 256 bytes, got %d", len(bytes)) } // From the 'official' RP2040 checksum script: @@ -1460,6 +1484,21 @@ func patchRP2040BootCRC(executable string) error { return replaceElfSection(executable, ".boot2", bytes) } +// RP2350 block patching. +func patchRP2350BootIMAGE_DEF(executable string) error { + boot2, _, err := getElfSectionData(executable, ".boot2") + if err != nil { + return err + } + item0 := picobin.MakeImageDef(picobin.ImageTypeExecutable, picobin.ExeSecSecure, picobin.ExeCPUARM, picobin.ExeChipRP2350, false) + newBoot := make([]byte, 256) + newBoot, _, err = picobin.AppendBlockFromItems(newBoot[:0], []picobin.Item{item0.Item}, boot2, 0) + off := len(newBoot) + newBoot, _, err = picobin.AppendFinalBlock(newBoot, -off) + // Update the .boot2 section to included the CRC + return replaceElfSection(executable, ".boot2", newBoot) +} + // lock may acquire a lock at the specified path. // It returns a function to release the lock. // If flock is not supported, it does nothing. @@ -1472,3 +1511,10 @@ func lock(path string) func() { return func() { flock.Close() } } + +func b2u8(b bool) uint8 { + if b { + return 1 + } + return 0 +} diff --git a/builder/objcopy.go b/builder/objcopy.go index cf10547b4d..8561ef5793 100644 --- a/builder/objcopy.go +++ b/builder/objcopy.go @@ -2,11 +2,11 @@ package builder import ( "debug/elf" - "io" + "fmt" "os" - "sort" "github.com/marcinbor85/gohex" + "github.com/soypat/tinyboot/build/elfutil" ) // maxPadBytes is the maximum allowed bytes to be padded in a rom extraction @@ -26,18 +26,12 @@ func (e objcopyError) Error() string { return e.Op + ": " + e.Err.Error() } -type progSlice []*elf.Prog - -func (s progSlice) Len() int { return len(s) } -func (s progSlice) Less(i, j int) bool { return s[i].Paddr < s[j].Paddr } -func (s progSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } - // extractROM extracts a firmware image and the first load address from the // given ELF file. It tries to emulate the behavior of objcopy. func extractROM(path string) (uint64, []byte, error) { f, err := elf.Open(path) if err != nil { - return 0, nil, objcopyError{"failed to open ELF file to extract text segment", err} + return 0, nil, objcopyError{Op: "failed to open ELF file to extract text segment", Err: err} } defer f.Close() @@ -47,62 +41,33 @@ func extractROM(path string) (uint64, []byte, error) { // > memory dump of the contents of the input object file. All symbols and // > relocation information will be discarded. The memory dump will start at // > the load address of the lowest section copied into the output file. - - // Find the lowest section address. - startAddr := ^uint64(0) - for _, section := range f.Sections { - if section.Type != elf.SHT_PROGBITS || section.Flags&elf.SHF_ALLOC == 0 { - continue - } - if section.Addr < startAddr { - startAddr = section.Addr - } + start, end, err := elfutil.ROMAddr(f) + if err != nil { + return 0, nil, objcopyError{Op: "failed to calculate ELF ROM addresses", Err: err} } - - progs := make(progSlice, 0, 2) - for _, prog := range f.Progs { - if prog.Type != elf.PT_LOAD || prog.Filesz == 0 || prog.Off == 0 { - continue - } - progs = append(progs, prog) + err = elfutil.EnsureROMContiguous(f, start, end, maxPadBytes) + if err != nil { + return 0, nil, objcopyError{Op: "checking if ELF ROM contiguous", Err: err} } - if len(progs) == 0 { - return 0, nil, objcopyError{"file does not contain ROM segments: " + path, nil} + const ( + _ = 1 << (iota * 10) + kB + MB + GB + ) + const maxSize = 1 * GB + if end-start > maxSize { + return 0, nil, objcopyError{Op: fmt.Sprintf("obj size exceeds max %d/%d, bad ELF address calculation?", end-start, maxSize)} } - sort.Sort(progs) - var rom []byte - for _, prog := range progs { - romEnd := progs[0].Paddr + uint64(len(rom)) - if prog.Paddr > romEnd && prog.Paddr < romEnd+16 { - // Sometimes, the linker seems to insert a bit of padding between - // segments. Simply zero-fill these parts. - rom = append(rom, make([]byte, prog.Paddr-romEnd)...) - } - if prog.Paddr != progs[0].Paddr+uint64(len(rom)) { - diff := prog.Paddr - (progs[0].Paddr + uint64(len(rom))) - if diff > maxPadBytes { - return 0, nil, objcopyError{"ROM segments are non-contiguous: " + path, nil} - } - // Pad the difference - rom = append(rom, make([]byte, diff)...) - } - data, err := io.ReadAll(prog.Open()) - if err != nil { - return 0, nil, objcopyError{"failed to extract segment from ELF file: " + path, err} - } - rom = append(rom, data...) - } - if progs[0].Paddr < startAddr { - // The lowest memory address is before the first section. This means - // that there is some extra data loaded at the start of the image that - // should be discarded. - // Example: ELF files where .text doesn't start at address 0 because - // there is a bootloader at the start. - return startAddr, rom[startAddr-progs[0].Paddr:], nil - } else { - return progs[0].Paddr, rom, nil + ROM := make([]byte, end-start) + n, err := elfutil.ReadROMAt(f, ROM, start) + if err != nil { + return 0, nil, objcopyError{Op: "reading ELF ROM", Err: err} + } else if n != len(ROM) { + return 0, nil, objcopyError{Op: "short ELF ROM read"} } + return start, ROM, nil } // objcopy converts an ELF file to a different (simpler) output file format: diff --git a/builder/uf2.go b/builder/uf2.go index 62bae5c66a..676acee09c 100644 --- a/builder/uf2.go +++ b/builder/uf2.go @@ -8,146 +8,34 @@ package builder // import ( - "bytes" - "encoding/binary" + "math" "os" - "strconv" + + "github.com/soypat/tinyboot/build/uf2" ) // convertELFFileToUF2File converts an ELF file to a UF2 file. func convertELFFileToUF2File(infile, outfile string, uf2FamilyID string) error { - // Read the .text segment. - targetAddress, data, err := extractROM(infile) - if err != nil { - return err + uf2Formatter := uf2.Formatter{ChunkSize: 256} + if uf2FamilyID != "" { + err := uf2Formatter.SetFamilyID(uf2FamilyID) + if err != nil { + return err + } } - - output, _, err := convertBinToUF2(data, uint32(targetAddress), uf2FamilyID) + start, ROM, err := extractROM(infile) if err != nil { return err + } else if start > math.MaxUint32 { + return objcopyError{Op: "ELF start ROM address overflows uint32"} } - return os.WriteFile(outfile, output, 0644) -} -// convertBinToUF2 converts the binary bytes in input to UF2 formatted data. -func convertBinToUF2(input []byte, targetAddr uint32, uf2FamilyID string) ([]byte, int, error) { - blocks := split(input, 256) - output := make([]byte, 0) - - bl, err := newUF2Block(targetAddr, uf2FamilyID) + // Write raw ROM contents in UF2 format to outfile. + expectBlocks := len(ROM)/int(uf2Formatter.ChunkSize) + 1 + uf2data := make([]byte, expectBlocks*uf2.BlockSize) + uf2data, _, err = uf2Formatter.AppendTo(uf2data[:0], ROM, uint32(start)) if err != nil { - return nil, 0, err - } - bl.SetNumBlocks(len(blocks)) - - for i := 0; i < len(blocks); i++ { - bl.SetBlockNo(i) - bl.SetData(blocks[i]) - - output = append(output, bl.Bytes()...) - bl.IncrementAddress(bl.payloadSize) - } - - return output, len(blocks), nil -} - -const ( - uf2MagicStart0 = 0x0A324655 // "UF2\n" - uf2MagicStart1 = 0x9E5D5157 // Randomly selected - uf2MagicEnd = 0x0AB16F30 // Ditto -) - -// uf2Block is the structure used for each UF2 code block sent to device. -type uf2Block struct { - magicStart0 uint32 - magicStart1 uint32 - flags uint32 - targetAddr uint32 - payloadSize uint32 - blockNo uint32 - numBlocks uint32 - familyID uint32 - data []uint8 - magicEnd uint32 -} - -// newUF2Block returns a new uf2Block struct that has been correctly populated -func newUF2Block(targetAddr uint32, uf2FamilyID string) (*uf2Block, error) { - var flags uint32 - var familyID uint32 - if uf2FamilyID != "" { - flags |= flagFamilyIDPresent - v, err := strconv.ParseUint(uf2FamilyID, 0, 32) - if err != nil { - return nil, err - } - familyID = uint32(v) - } - return &uf2Block{magicStart0: uf2MagicStart0, - magicStart1: uf2MagicStart1, - magicEnd: uf2MagicEnd, - targetAddr: targetAddr, - flags: flags, - familyID: familyID, - payloadSize: 256, - data: make([]byte, 476), - }, nil -} - -const ( - flagFamilyIDPresent = 0x00002000 -) - -// Bytes converts the uf2Block to a slice of bytes that can be written to file. -func (b *uf2Block) Bytes() []byte { - buf := bytes.NewBuffer(make([]byte, 0, 512)) - binary.Write(buf, binary.LittleEndian, b.magicStart0) - binary.Write(buf, binary.LittleEndian, b.magicStart1) - binary.Write(buf, binary.LittleEndian, b.flags) - binary.Write(buf, binary.LittleEndian, b.targetAddr) - binary.Write(buf, binary.LittleEndian, b.payloadSize) - binary.Write(buf, binary.LittleEndian, b.blockNo) - binary.Write(buf, binary.LittleEndian, b.numBlocks) - binary.Write(buf, binary.LittleEndian, b.familyID) - binary.Write(buf, binary.LittleEndian, b.data) - binary.Write(buf, binary.LittleEndian, b.magicEnd) - - return buf.Bytes() -} - -// IncrementAddress moves the target address pointer forward by count bytes. -func (b *uf2Block) IncrementAddress(count uint32) { - b.targetAddr += b.payloadSize -} - -// SetData sets the data to be used for the current block. -func (b *uf2Block) SetData(d []byte) { - b.data = make([]byte, 476) - copy(b.data[:], d) -} - -// SetBlockNo sets the current block number to be used. -func (b *uf2Block) SetBlockNo(bn int) { - b.blockNo = uint32(bn) -} - -// SetNumBlocks sets the total number of blocks for this UF2 file. -func (b *uf2Block) SetNumBlocks(total int) { - b.numBlocks = uint32(total) -} - -// split splits a slice of bytes into a slice of byte slices of a specific size limit. -func split(input []byte, limit int) [][]byte { - var block []byte - output := make([][]byte, 0, len(input)/limit+1) - for len(input) >= limit { - // add all blocks - block, input = input[:limit], input[limit:] - output = append(output, block) - } - if len(input) > 0 { - // add remaining block (that isn't full sized) - output = append(output, input) + return objcopyError{Op: "writing UF2 blocks", Err: err} } - return output + return os.WriteFile(outfile, uf2data, 0644) } diff --git a/compileopts/target.go b/compileopts/target.go index 41a7babd91..26b78f586b 100644 --- a/compileopts/target.go +++ b/compileopts/target.go @@ -45,6 +45,7 @@ type TargetSpec struct { LinkerScript string `json:"linkerscript,omitempty"` ExtraFiles []string `json:"extra-files,omitempty"` RP2040BootPatch *bool `json:"rp2040-boot-patch,omitempty"` // Patch RP2040 2nd stage bootloader checksum + BootPatches []string `json:"boot-patches,omitempty"` // Bootloader patches to be applied in the order they appear. Emulator string `json:"emulator,omitempty"` FlashCommand string `json:"flash-command,omitempty"` GDB []string `json:"gdb,omitempty"` diff --git a/go.mod b/go.mod index bf85ef3ad1..ea75e1959e 100644 --- a/go.mod +++ b/go.mod @@ -31,6 +31,7 @@ require ( github.com/josharian/intern v1.0.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-isatty v0.0.20 // indirect + github.com/soypat/tinyboot v0.0.0-20240922191516-a0ab175013d3 // indirect github.com/stretchr/testify v1.8.4 // indirect golang.org/x/text v0.16.0 // indirect ) diff --git a/go.sum b/go.sum index 7a6d1f4a97..65c737fd24 100644 --- a/go.sum +++ b/go.sum @@ -48,6 +48,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3 h1:aQKxg3+2p+IFXXg97McgDGT5zcMrQoi0EICZs8Pgchs= github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3/go.mod h1:9/etS5gpQq9BJsJMWg1wpLbfuSnkm8dPF6FdW2JXVhA= +github.com/soypat/tinyboot v0.0.0-20240922191516-a0ab175013d3 h1:qTeQ5xG5DQUW01KWdoMJ/XbZp8InTm1Mz7nR7Qfv4D0= +github.com/soypat/tinyboot v0.0.0-20240922191516-a0ab175013d3/go.mod h1:OsayN6hjGmz9VPyFHqVH6OSAUvUjXAcMviO3YS336hA= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= go.bug.st/serial v1.6.0 h1:mAbRGN4cKE2J5gMwsMHC2KQisdLRQssO9WSM+rbZJ8A= diff --git a/src/machine/machine_rp2040.go b/src/machine/machine_rp2.go similarity index 71% rename from src/machine/machine_rp2040.go rename to src/machine/machine_rp2.go index 45f9f510f5..1d01866bd0 100644 --- a/src/machine/machine_rp2040.go +++ b/src/machine/machine_rp2.go @@ -1,15 +1,44 @@ -//go:build rp2040 +//go:build rp2040 || rp2350 package machine import ( "device/rp" + "runtime/interrupt" "runtime/volatile" "unsafe" ) const deviceName = rp.Device +const ( + // Number of spin locks available + _NUMSPINLOCKS = 32 + // Number of interrupt handlers available + _NUMIRQ = 32 + _PICO_SPINLOCK_ID_IRQ = 9 +) + +// UART on the RP2040 +var ( + UART0 = &_UART0 + _UART0 = UART{ + Buffer: NewRingBuffer(), + Bus: rp.UART0, + } + + UART1 = &_UART1 + _UART1 = UART{ + Buffer: NewRingBuffer(), + Bus: rp.UART1, + } +) + +func init() { + UART0.Interrupt = interrupt.New(rp.IRQ_UART0_IRQ, _UART0.handleInterrupt) + UART1.Interrupt = interrupt.New(rp.IRQ_UART1_IRQ, _UART1.handleInterrupt) +} + //go:linkname machineInit runtime.machineInit func machineInit() { // Reset all peripherals to put system into a known state, @@ -26,13 +55,7 @@ func machineInit() { // Remove reset from peripherals which are clocked only by clkSys and // clkRef. Other peripherals stay in reset until we've configured clocks. - bits = ^uint32(rp.RESETS_RESET_ADC | - rp.RESETS_RESET_RTC | - rp.RESETS_RESET_SPI0 | - rp.RESETS_RESET_SPI1 | - rp.RESETS_RESET_UART0 | - rp.RESETS_RESET_UART1 | - rp.RESETS_RESET_USBCTRL) + bits = ^uint32(initUnreset) unresetBlockWait(bits) clocks.init() @@ -94,4 +117,25 @@ const ( ) // DMA channels usable on the RP2040. -var dmaChannels = (*[12]dmaChannel)(unsafe.Pointer(rp.DMA)) +var dmaChannels = (*[12 + 4*rp2350ExtraReg]dmaChannel)(unsafe.Pointer(rp.DMA)) + +//go:inline +func boolToBit(a bool) uint32 { + if a { + return 1 + } + return 0 +} + +//go:inline +func u32max(a, b uint32) uint32 { + if a > b { + return a + } + return b +} + +//go:inline +func isReservedI2CAddr(addr uint8) bool { + return (addr&0x78) == 0 || (addr&0x78) == 0x78 +} diff --git a/src/machine/machine_rp2040_i2c.go b/src/machine/machine_rp2040_i2c.go index b7dc63d2b2..f34aa259f5 100644 --- a/src/machine/machine_rp2040_i2c.go +++ b/src/machine/machine_rp2040_i2c.go @@ -631,24 +631,3 @@ func (b i2cAbortError) Reasons() (reasons []string) { } return reasons } - -//go:inline -func boolToBit(a bool) uint32 { - if a { - return 1 - } - return 0 -} - -//go:inline -func u32max(a, b uint32) uint32 { - if a > b { - return a - } - return b -} - -//go:inline -func isReservedI2CAddr(addr uint8) bool { - return (addr&0x78) == 0 || (addr&0x78) == 0x78 -} diff --git a/src/machine/machine_rp2350_resets.go b/src/machine/machine_rp2350_resets.go new file mode 100644 index 0000000000..830fc13468 --- /dev/null +++ b/src/machine/machine_rp2350_resets.go @@ -0,0 +1,44 @@ +//go:build rp2350 + +package machine + +import ( + "device/rp" + "runtime/volatile" + "unsafe" +) + +// RESETS_RESET_Msk is bitmask to reset all peripherals +// +// TODO: This field is not available in the device file. +const RESETS_RESET_Msk = 0x01ffffff + +type resetsType struct { + frceOn volatile.Register32 + frceOff volatile.Register32 + wdSel volatile.Register32 + resetDone volatile.Register32 +} + +var resets = (*resetsType)(unsafe.Pointer(rp.RESETS)) + +// resetBlock resets hardware blocks specified +// by the bit pattern in bits. +func resetBlock(bits uint32) { + resets.frceOff.Set(bits) +} + +// unresetBlock brings hardware blocks specified by the +// bit pattern in bits out of reset. +func unresetBlock(bits uint32) { + resets.frceOn.Set(bits) +} + +// unresetBlockWait brings specified hardware blocks +// specified by the bit pattern in bits +// out of reset and wait for completion. +func unresetBlockWait(bits uint32) { + unresetBlock(bits) + for !resets.resetDone.HasBits(bits) { + } +} diff --git a/src/machine/machine_rp2_2040.go b/src/machine/machine_rp2_2040.go new file mode 100644 index 0000000000..f42a23c7ee --- /dev/null +++ b/src/machine/machine_rp2_2040.go @@ -0,0 +1,148 @@ +//go:build rp2040 + +package machine + +import ( + "device/rp" + "unsafe" +) + +const ( + _NUMBANK0_GPIOS = 30 + _NUMBANK0_IRQS = 4 + rp2350ExtraReg = 0 + initUnreset = rp.RESETS_RESET_ADC | + rp.RESETS_RESET_RTC | + rp.RESETS_RESET_SPI0 | + rp.RESETS_RESET_SPI1 | + rp.RESETS_RESET_UART0 | + rp.RESETS_RESET_UART1 | + rp.RESETS_RESET_USBCTRL +) + +const ( + PinOutput PinMode = iota + PinInput + PinInputPulldown + PinInputPullup + PinAnalog + PinUART + PinPWM + PinI2C + PinSPI + PinPIO0 + PinPIO1 +) + +// GPIO function selectors +const ( + fnJTAG pinFunc = 0 + fnSPI pinFunc = 1 // Connect one of the internal PL022 SPI peripherals to GPIO + fnUART pinFunc = 2 + fnI2C pinFunc = 3 + // Connect a PWM slice to GPIO. There are eight PWM slices, + // each with two outputchannels (A/B). The B pin can also be used as an input, + // for frequency and duty cyclemeasurement + fnPWM pinFunc = 4 + // Software control of GPIO, from the single-cycle IO (SIO) block. + // The SIO function (F5)must be selected for the processors to drive a GPIO, + // but the input is always connected,so software can check the state of GPIOs at any time. + fnSIO pinFunc = 5 + // Connect one of the programmable IO blocks (PIO) to GPIO. PIO can implement a widevariety of interfaces, + // and has its own internal pin mapping hardware, allowing flexibleplacement of digital interfaces on bank 0 GPIOs. + // The PIO function (F6, F7) must beselected for PIO to drive a GPIO, but the input is always connected, + // so the PIOs canalways see the state of all pins. + fnPIO0, fnPIO1 pinFunc = 6, 7 + // General purpose clock inputs/outputs. Can be routed to a number of internal clock domains onRP2040, + // e.g. Input: to provide a 1 Hz clock for the RTC, or can be connected to an internalfrequency counter. + // e.g. Output: optional integer divide + fnGPCK pinFunc = 8 + // USB power control signals to/from the internal USB controller + fnUSB pinFunc = 9 + fnNULL pinFunc = 0x1f + + fnXIP pinFunc = 0 +) + +// Configure configures the gpio pin as per mode. +func (p Pin) Configure(config PinConfig) { + if p == NoPin { + return + } + p.init() + mask := uint32(1) << p + switch config.Mode { + case PinOutput: + p.setFunc(fnSIO) + rp.SIO.GPIO_OE_SET.Set(mask) + case PinInput: + p.setFunc(fnSIO) + p.pulloff() + case PinInputPulldown: + p.setFunc(fnSIO) + p.pulldown() + case PinInputPullup: + p.setFunc(fnSIO) + p.pullup() + case PinAnalog: + p.setFunc(fnNULL) + p.pulloff() + case PinUART: + p.setFunc(fnUART) + case PinPWM: + p.setFunc(fnPWM) + case PinI2C: + // IO config according to 4.3.1.3 of rp2040 datasheet. + p.setFunc(fnI2C) + p.pullup() + p.setSchmitt(true) + p.setSlew(false) + case PinSPI: + p.setFunc(fnSPI) + case PinPIO0: + p.setFunc(fnPIO0) + case PinPIO1: + p.setFunc(fnPIO1) + } +} + +var ( + timer = (*timerType)(unsafe.Pointer(rp.TIMER)) +) + +// Enable or disable a specific interrupt on the executing core. +// num is the interrupt number which must be in [0,31]. +func irqSet(num uint32, enabled bool) { + if num >= _NUMIRQ { + return + } + irqSetMask(1<= _NUMIRQ { + return + } + irqSetMask(1<>(4*(p%8))) & 0xf } - -// UART on the RP2040 -var ( - UART0 = &_UART0 - _UART0 = UART{ - Buffer: NewRingBuffer(), - Bus: rp.UART0, - } - - UART1 = &_UART1 - _UART1 = UART{ - Buffer: NewRingBuffer(), - Bus: rp.UART1, - } -) - -func init() { - UART0.Interrupt = interrupt.New(rp.IRQ_UART0_IRQ, _UART0.handleInterrupt) - UART1.Interrupt = interrupt.New(rp.IRQ_UART1_IRQ, _UART1.handleInterrupt) -} diff --git a/src/machine/machine_rp2040_pins.go b/src/machine/machine_rp2_pins.go similarity index 85% rename from src/machine/machine_rp2040_pins.go rename to src/machine/machine_rp2_pins.go index 9abbdb002e..93f2d50a01 100644 --- a/src/machine/machine_rp2040_pins.go +++ b/src/machine/machine_rp2_pins.go @@ -1,4 +1,4 @@ -//go:build rp2040 || ae_rp2040 || badger2040 || challenger_rp2040 || feather_rp2040 || gopher_badge || kb2040 || macropad_rp2040 || nano_rp2040 || pico || qtpy_rp2040 || thingplus_rp2040 || thumby || tufty2040 || waveshare_rp2040_zero || xiao_rp2040 +//go:build rp2040 || rp2350 || ae_rp2040 || badger2040 || challenger_rp2040 || feather_rp2040 || gopher_badge || kb2040 || macropad_rp2040 || nano_rp2040 || pico || qtpy_rp2040 || thingplus_rp2040 || thumby || tufty2040 || waveshare_rp2040_zero || xiao_rp2040 package machine diff --git a/src/machine/machine_rp2040_pll.go b/src/machine/machine_rp2_pll.go similarity index 98% rename from src/machine/machine_rp2040_pll.go rename to src/machine/machine_rp2_pll.go index d611f2924d..f409768ab3 100644 --- a/src/machine/machine_rp2040_pll.go +++ b/src/machine/machine_rp2_pll.go @@ -1,4 +1,4 @@ -//go:build rp2040 +//go:build rp2040 || rp2350 package machine diff --git a/src/machine/machine_rp2040_sync.go b/src/machine/machine_rp2_sync.go similarity index 62% rename from src/machine/machine_rp2040_sync.go rename to src/machine/machine_rp2_sync.go index 4c9b443237..5019552afd 100644 --- a/src/machine/machine_rp2040_sync.go +++ b/src/machine/machine_rp2_sync.go @@ -1,24 +1,11 @@ -//go:build rp2040 +//go:build rp2040 || rp2350 package machine -import ( - "device/rp" -) - // machine_rp2040_sync.go contains interrupt and // lock primitives similar to those found in Pico SDK's // irq.c -const ( - // Number of spin locks available - _NUMSPINLOCKS = 32 - // Number of interrupt handlers available - _NUMIRQ = 32 - _PICO_SPINLOCK_ID_IRQ = 9 - _NUMBANK0_GPIOS = 30 -) - // Clears interrupt flag on a pin func (p Pin) acknowledgeInterrupt(change PinChange) { ioBank0.intR[p>>3].Set(p.ioIntBit(change)) @@ -50,23 +37,3 @@ func (p Pin) ctrlSetInterrupt(change PinChange, enabled bool, base *irqCtrl) { enReg.ClearBits(p.ioIntBit(change)) } } - -// Enable or disable a specific interrupt on the executing core. -// num is the interrupt number which must be in [0,31]. -func irqSet(num uint32, enabled bool) { - if num >= _NUMIRQ { - return - } - irqSetMask(1< BOOT2_TEXT = 0x0 + + /* The second stage will always enter the image at the start of .text. + The debugger will use the ELF entry point, which is the _entry_point + symbol if present, otherwise defaults to start of .text. + This can be used to transfer control back to the bootrom on debugger + launches only, to perform proper flash setup. + */ +} + +INCLUDE "targets/arm.ld" diff --git a/targets/rp2350_boot2_generic03h.S b/targets/rp2350_boot2_generic03h.S new file mode 100644 index 0000000000..70b861afc4 --- /dev/null +++ b/targets/rp2350_boot2_generic03h.S @@ -0,0 +1,461 @@ +// ---------------------------------------------------------------------------- +// Second stage boot code +// Copyright (c) 2019-2021 Raspberry Pi (Trading) Ltd. +// SPDX-License-Identifier: BSD-3-Clause +// +// Device: Anything which responds to 03h serial read command +// +// Details: * Configure SSI to translate each APB read into a 03h command +// * 8 command clocks, 24 address clocks and 32 data clocks +// * This enables you to boot from almost anything: you can pretty +// much solder a potato to your PCB, or a piece of cheese +// * The tradeoff is performance around 3x worse than QSPI XIP +// +// Building: * This code must be position-independent, and use stack only +// * The code will be padded to a size of 256 bytes, including a +// 4-byte checksum. Therefore code size cannot exceed 252 bytes. +// ---------------------------------------------------------------------------- + +// #include "pico.h" // https://github.com/raspberrypi/pico-sdk/blob/master/src/boards/include/boards/pico.h +// Figure out what boot uses... + +// #include "pico/asm_helper.S" // https://github.com/raspberrypi/pico-sdk/blob/master/src/rp2350/pico_platform/include/pico/asm_helper.S +#if !PICO_ASSEMBLER_IS_CLANG +#define apsr_nzcv r15 +#endif +# note we don't do this by default in this file for backwards comaptibility with user code +# that may include this file, but not use unified syntax. Note that this macro does equivalent +# setup to the pico_default_asm macro for inline assembly in C code. +.macro pico_default_asm_setup +#ifndef __riscv +.syntax unified // Selects Unified Assembly Language (UAL) syntax for assembler. Unifies ARM and thumb into single syntax. +.cpu cortex-m33 // Specify target CPU architecture. +.fpu fpv5-sp-d16 // Specify the type of FPU available on CPU. Supports SP (single precision) operations with 16 double precision registers. +.thumb // Assemble subsequent code with Thumb instruction set, the compact 16-bit encoding of the most frequently used 32-bit ARM instructions. +.section .boot2, "ax" // Defines a new section named .boot2 with attributes 'a': Allocatable-This section occupies space in memory image, and 'x': This section contains executable code. +// Code and data assembled after this point will be placed in .boot2 section. The Linker script can specify where this section should be located in memory, such as at a specific address. +// We need to ensure this code is placed at the very start of flash. See rp2350.ld, .boot2 is placed at 0x10000000 and has max length 256. +// The compiled code cannot exceed 256 bytes! +// One can check contents by compiling and using objdump to visualize the code: +// tinygo build -target=rp2350 -o=rp2350.elf -serial=none examples/blinky1 +// objdump -s -j .boot2 rp2350.elf +#endif +.endm + +.macro regular_func fnname +.global \fnname // Declares fnname as a global symbol. +.type \fnname,%function // Specifies fnname is a type of function. Helps tools understand fnname is executable code, not data. +#ifndef __riscv +.thumb_func // Indicate fnname is a thumb function. +#endif +\fnname: // Mark point of entry for function. +.endm + + +// #include "hardware/platform_defs.h" // https://github.com/raspberrypi/pico-sdk/blob/master/src/rp2350/hardware_regs/include/hardware/platform_defs.h +#ifndef _u +#ifdef __ASSEMBLER__ +#define _u(x) x +#else +#define _u(x) x ## u +#endif +#endif + +// #include "hardware/regs/addressmap.h" https://github.com/raspberrypi/pico-sdk/blob/master/src/rp2350/hardware_regs/include/hardware/regs/addressmap.h +// Register address offsets for atomic RMW aliases +#define REG_ALIAS_RW_BITS (_u(0x0) << _u(12)) +#define REG_ALIAS_XOR_BITS (_u(0x1) << _u(12)) +#define REG_ALIAS_SET_BITS (_u(0x2) << _u(12)) +#define REG_ALIAS_CLR_BITS (_u(0x3) << _u(12)) + +#define ROM_BASE _u(0x00000000) +#define XIP_BASE _u(0x10000000) +#define XIP_SRAM_BASE _u(0x13ffc000) +#define XIP_END _u(0x14000000) +#define XIP_NOCACHE_NOALLOC_BASE _u(0x14000000) +#define XIP_SRAM_END _u(0x14000000) +#define XIP_NOCACHE_NOALLOC_END _u(0x18000000) +#define XIP_MAINTENANCE_BASE _u(0x18000000) +#define XIP_NOCACHE_NOALLOC_NOTRANSLATE_BASE _u(0x1c000000) +#define SRAM0_BASE _u(0x20000000) +#define XIP_NOCACHE_NOALLOC_NOTRANSLATE_END _u(0x20000000) +#define SRAM_BASE _u(0x20000000) +#define SRAM_STRIPED_BASE _u(0x20000000) +#define SRAM4_BASE _u(0x20040000) +#define SRAM8_BASE _u(0x20080000) +#define SRAM_STRIPED_END _u(0x20080000) +#define SRAM_SCRATCH_X_BASE _u(0x20080000) +#define SRAM9_BASE _u(0x20081000) +#define SRAM_SCRATCH_Y_BASE _u(0x20081000) +#define SRAM_END _u(0x20082000) +#define SYSINFO_BASE _u(0x40000000) +#define SYSCFG_BASE _u(0x40008000) +#define CLOCKS_BASE _u(0x40010000) +#define PSM_BASE _u(0x40018000) +#define RESETS_BASE _u(0x40020000) +#define IO_BANK0_BASE _u(0x40028000) +#define IO_QSPI_BASE _u(0x40030000) +#define PADS_BANK0_BASE _u(0x40038000) +#define PADS_QSPI_BASE _u(0x40040000) +#define XOSC_BASE _u(0x40048000) +#define PLL_SYS_BASE _u(0x40050000) +#define PLL_USB_BASE _u(0x40058000) +#define ACCESSCTRL_BASE _u(0x40060000) +#define BUSCTRL_BASE _u(0x40068000) +#define UART0_BASE _u(0x40070000) +#define UART1_BASE _u(0x40078000) +#define SPI0_BASE _u(0x40080000) +#define SPI1_BASE _u(0x40088000) +#define I2C0_BASE _u(0x40090000) +#define I2C1_BASE _u(0x40098000) +#define ADC_BASE _u(0x400a0000) +#define PWM_BASE _u(0x400a8000) +#define TIMER0_BASE _u(0x400b0000) +#define TIMER1_BASE _u(0x400b8000) +#define HSTX_CTRL_BASE _u(0x400c0000) +#define XIP_CTRL_BASE _u(0x400c8000) +#define XIP_QMI_BASE _u(0x400d0000) +#define WATCHDOG_BASE _u(0x400d8000) +#define BOOTRAM_BASE _u(0x400e0000) +#define BOOTRAM_END _u(0x400e0400) +#define ROSC_BASE _u(0x400e8000) +#define TRNG_BASE _u(0x400f0000) +#define SHA256_BASE _u(0x400f8000) +#define POWMAN_BASE _u(0x40100000) +#define TICKS_BASE _u(0x40108000) +#define OTP_BASE _u(0x40120000) +#define OTP_DATA_BASE _u(0x40130000) +#define OTP_DATA_RAW_BASE _u(0x40134000) +#define OTP_DATA_GUARDED_BASE _u(0x40138000) +#define OTP_DATA_RAW_GUARDED_BASE _u(0x4013c000) +#define CORESIGHT_PERIPH_BASE _u(0x40140000) +#define CORESIGHT_ROMTABLE_BASE _u(0x40140000) +#define CORESIGHT_AHB_AP_CORE0_BASE _u(0x40142000) +#define CORESIGHT_AHB_AP_CORE1_BASE _u(0x40144000) +#define CORESIGHT_TIMESTAMP_GEN_BASE _u(0x40146000) +#define CORESIGHT_ATB_FUNNEL_BASE _u(0x40147000) +#define CORESIGHT_TPIU_BASE _u(0x40148000) +#define CORESIGHT_CTI_BASE _u(0x40149000) +#define CORESIGHT_APB_AP_RISCV_BASE _u(0x4014a000) +#define DFT_BASE _u(0x40150000) +#define GLITCH_DETECTOR_BASE _u(0x40158000) +#define TBMAN_BASE _u(0x40160000) +#define DMA_BASE _u(0x50000000) +#define USBCTRL_BASE _u(0x50100000) +#define USBCTRL_DPRAM_BASE _u(0x50100000) +#define USBCTRL_REGS_BASE _u(0x50110000) +#define PIO0_BASE _u(0x50200000) +#define PIO1_BASE _u(0x50300000) +#define PIO2_BASE _u(0x50400000) +#define XIP_AUX_BASE _u(0x50500000) +#define HSTX_FIFO_BASE _u(0x50600000) +#define CORESIGHT_TRACE_BASE _u(0x50700000) +#define SIO_BASE _u(0xd0000000) +#define SIO_NONSEC_BASE _u(0xd0020000) +#define PPB_BASE _u(0xe0000000) +#define PPB_NONSEC_BASE _u(0xe0020000) +#define EPPB_BASE _u(0xe0080000) + +// #include "hardware/regs/qmi.h" // https://github.com/raspberrypi/pico-sdk/blob/master/src/rp2350/hardware_regs/include/hardware/regs/qmi.h +// Thats a lot of content... +#define QMI_M0_TIMING_CLKDIV_LSB _u(0) +#define QMI_M0_TIMING_CLKDIV_BITS _u(0x000000ff) +#define QMI_M0_TIMING_RXDELAY_LSB _u(8) +#define QMI_M0_TIMING_RXDELAY_BITS _u(0x00000700) +#define QMI_M0_TIMING_COOLDOWN_RESET _u(0x1) +#define QMI_M0_TIMING_COOLDOWN_BITS _u(0xc0000000) +#define QMI_M0_TIMING_COOLDOWN_MSB _u(31) +#define QMI_M0_TIMING_COOLDOWN_LSB _u(30) +#define QMI_M0_TIMING_COOLDOWN_ACCESS "RW" +#define QMI_M0_RCMD_PREFIX_RESET _u(0x03) +#define QMI_M0_RCMD_PREFIX_BITS _u(0x000000ff) +#define QMI_M0_RCMD_PREFIX_MSB _u(7) +#define QMI_M0_RCMD_PREFIX_LSB _u(0) +#define QMI_M0_RCMD_PREFIX_ACCESS "RW" +#define QMI_M0_RFMT_PREFIX_WIDTH_RESET _u(0x0) +#define QMI_M0_RFMT_PREFIX_WIDTH_BITS _u(0x00000003) +#define QMI_M0_RFMT_PREFIX_WIDTH_MSB _u(1) +#define QMI_M0_RFMT_PREFIX_WIDTH_LSB _u(0) +#define QMI_M0_RFMT_PREFIX_WIDTH_ACCESS "RW" +#define QMI_M0_RFMT_PREFIX_WIDTH_VALUE_S _u(0x0) +#define QMI_M0_RFMT_PREFIX_WIDTH_VALUE_D _u(0x1) +#define QMI_M0_RFMT_PREFIX_WIDTH_VALUE_Q _u(0x2) + +#define QMI_M0_RFMT_ADDR_WIDTH_RESET _u(0x0) +#define QMI_M0_RFMT_ADDR_WIDTH_BITS _u(0x0000000c) +#define QMI_M0_RFMT_ADDR_WIDTH_MSB _u(3) +#define QMI_M0_RFMT_ADDR_WIDTH_LSB _u(2) +#define QMI_M0_RFMT_ADDR_WIDTH_ACCESS "RW" +#define QMI_M0_RFMT_ADDR_WIDTH_VALUE_S _u(0x0) +#define QMI_M0_RFMT_ADDR_WIDTH_VALUE_D _u(0x1) +#define QMI_M0_RFMT_ADDR_WIDTH_VALUE_Q _u(0x2) + +#define QMI_M0_RFMT_SUFFIX_WIDTH_RESET _u(0x0) +#define QMI_M0_RFMT_SUFFIX_WIDTH_BITS _u(0x00000030) +#define QMI_M0_RFMT_SUFFIX_WIDTH_MSB _u(5) +#define QMI_M0_RFMT_SUFFIX_WIDTH_LSB _u(4) +#define QMI_M0_RFMT_SUFFIX_WIDTH_ACCESS "RW" +#define QMI_M0_RFMT_SUFFIX_WIDTH_VALUE_S _u(0x0) +#define QMI_M0_RFMT_SUFFIX_WIDTH_VALUE_D _u(0x1) +#define QMI_M0_RFMT_SUFFIX_WIDTH_VALUE_Q _u(0x2) + +#define QMI_M0_RFMT_DUMMY_WIDTH_RESET _u(0x0) +#define QMI_M0_RFMT_DUMMY_WIDTH_BITS _u(0x000000c0) +#define QMI_M0_RFMT_DUMMY_WIDTH_MSB _u(7) +#define QMI_M0_RFMT_DUMMY_WIDTH_LSB _u(6) +#define QMI_M0_RFMT_DUMMY_WIDTH_ACCESS "RW" +#define QMI_M0_RFMT_DUMMY_WIDTH_VALUE_S _u(0x0) +#define QMI_M0_RFMT_DUMMY_WIDTH_VALUE_D _u(0x1) +#define QMI_M0_RFMT_DUMMY_WIDTH_VALUE_Q _u(0x2) + +#define QMI_M0_RFMT_DATA_WIDTH_RESET _u(0x0) +#define QMI_M0_RFMT_DATA_WIDTH_BITS _u(0x00000300) +#define QMI_M0_RFMT_DATA_WIDTH_MSB _u(9) +#define QMI_M0_RFMT_DATA_WIDTH_LSB _u(8) +#define QMI_M0_RFMT_DATA_WIDTH_ACCESS "RW" +#define QMI_M0_RFMT_DATA_WIDTH_VALUE_S _u(0x0) +#define QMI_M0_RFMT_DATA_WIDTH_VALUE_D _u(0x1) +#define QMI_M0_RFMT_DATA_WIDTH_VALUE_Q _u(0x2) + +#define QMI_M0_RFMT_PREFIX_LEN_RESET _u(0x1) +#define QMI_M0_RFMT_PREFIX_LEN_BITS _u(0x00001000) +#define QMI_M0_RFMT_PREFIX_LEN_MSB _u(12) +#define QMI_M0_RFMT_PREFIX_LEN_LSB _u(12) +#define QMI_M0_RFMT_PREFIX_LEN_ACCESS "RW" +#define QMI_M0_RFMT_PREFIX_LEN_VALUE_NONE _u(0x0) +#define QMI_M0_RFMT_PREFIX_LEN_VALUE_8 _u(0x1) + +#define QMI_M0_RCMD_OFFSET _u(0x00000014) +#define QMI_M0_RCMD_BITS _u(0x0000ffff) +#define QMI_M0_RCMD_RESET _u(0x0000a003) + +#define QMI_M0_TIMING_OFFSET _u(0x0000000c) +#define QMI_M0_TIMING_BITS _u(0xf3fff7ff) +#define QMI_M0_TIMING_RESET _u(0x40000004) + +#define QMI_M0_RFMT_OFFSET _u(0x00000010) +#define QMI_M0_RFMT_BITS _u(0x1007d3ff) +#define QMI_M0_RFMT_RESET _u(0x00001000) + +// ---------------------------------------------------------------------------- +// Config section +// ---------------------------------------------------------------------------- +// It should be possible to support most flash devices by modifying this section + +// The serial flash interface will run at clk_sys/PICO_FLASH_SPI_CLKDIV. +// This must be a positive integer. +// The bootrom is very conservative with SPI frequency, but here we should be +// as aggressive as possible. + +#ifndef PICO_FLASH_SPI_CLKDIV +#define PICO_FLASH_SPI_CLKDIV 4 +#endif +#if (PICO_FLASH_SPI_CLKDIV << QMI_M0_TIMING_CLKDIV_LSB) & ~QMI_M0_TIMING_CLKDIV_BITS +#error "CLKDIV greater than maximum" +#endif + +// RX sampling delay is measured in units of one half clock cycle. + +#ifndef PICO_FLASH_SPI_RXDELAY +#define PICO_FLASH_SPI_RXDELAY 1 +#endif +#if (PICO_FLASH_SPI_RXDELAY << QMI_M0_TIMING_RXDELAY_LSB) & ~QMI_M0_TIMING_RXDELAY_BITS +#error "RX delay greater than maximum" +#endif + +#define CMD_READ 0x03 + +// ---------------------------------------------------------------------------- +// Register initialisation values -- same in Arm/RISC-V code. +// ---------------------------------------------------------------------------- + +// The QMI is automatically configured for 03h XIP straight out of reset, +// but this code can't assume it's still in that state. Set up memory +// window 0 for 03h serial reads. + +// Setup timing parameters: short sequential-access cooldown, configured +// CLKDIV and RXDELAY, and no constraints on CS max assertion, CS min +// deassertion, or page boundary burst breaks. + + +#define INIT_M0_TIMING ((1 << QMI_M0_TIMING_COOLDOWN_LSB) |(PICO_FLASH_SPI_RXDELAY << QMI_M0_TIMING_RXDELAY_LSB) |(PICO_FLASH_SPI_CLKDIV << QMI_M0_TIMING_CLKDIV_LSB) |0) + +// Set command constants +#define INIT_M0_RCMD (CMD_READ << QMI_M0_RCMD_PREFIX_LSB | 0) + +// Set read format to all-serial with a command prefix +#define INIT_M0_RFMT ((QMI_M0_RFMT_PREFIX_WIDTH_VALUE_S << QMI_M0_RFMT_PREFIX_WIDTH_LSB) | (QMI_M0_RFMT_ADDR_WIDTH_VALUE_S << QMI_M0_RFMT_ADDR_WIDTH_LSB) | (QMI_M0_RFMT_SUFFIX_WIDTH_VALUE_S << QMI_M0_RFMT_SUFFIX_WIDTH_LSB) | (QMI_M0_RFMT_DUMMY_WIDTH_VALUE_S << QMI_M0_RFMT_DUMMY_WIDTH_LSB) | (QMI_M0_RFMT_DATA_WIDTH_VALUE_S << QMI_M0_RFMT_DATA_WIDTH_LSB) | (QMI_M0_RFMT_PREFIX_LEN_VALUE_8 << QMI_M0_RFMT_PREFIX_LEN_LSB) | 0) + + +// load_r0 loads the register's value at address regaddr into R0 by stepping on r1 with the regaddr. +.macro load_r0 regaddr + ldr r1, =\regaddr // Load immediate value of regaddr with = pseudoinstr. May translate into several instructions depending on size of integer. + ldr r0, [r1] // Load memory at addr described by a register. +.endm + + +// fload_r0 loads the register's value at address regaddr into R0 and leaves rest of registers in same state. +.macro fload_r0 regaddr + push {r1} // Pushes contents of r1 onto stack. Stack grows downward in Cortex arch. + load_r0 regaddr + pop {r1} // Pops stack onto register r1. Braces can contain a range of addresses. +.endm + +// store_off_r3 is a helper that stores value into a register at address given by r3+off. Uses r0. +.macro store_off_r3 value, off + ldr r0, =\value // Store literal value we want to store at address r3+off into r0. + str r0, [r3, #\off] // Store value r0 into register at r3+off. +.endm + +// wait_bitclr checks value at address specified by register rx against bitmask until the AND between them yields 0. Steps on r0. +.macro wait_bitclr rx, bitmask +1: + tst \rx, #\bitmask // Perform bitwise AND between register and operand (register or immediate value) updating condition flags. Z flag set if AND yields zero. + bne 1b // Conditionally branch if Z flag set. BNE: Branch if Not Equal -> Will branch if Z set, so if r0&bitmask == 0. +.endm + +#define USESIO SIO_BASE +#define SIO_GPIOOUTSET_OFFSET _u(0x18) +#define SIO_GPIOOUTCLR_OFFSET _u(0x20) +#define pinfnSIO _u(1<<5) +#define pinfnUART _u(1<<2) +.macro pin_init pin, pinfn + #define pinmask (1<<\pin) + #define SIO_GPIOOECLR_OFFSET _u(0x40) + #define SIO_GPIOOESET_OFFSET _u(0x38) + #define pdtctrl_replace_bits _u(0x40|) + #define PADSBNK_IOPIN_OFFSET _u(4+4*\pin) + #define IOCTLREG _u(IO_BANK0_BASE+8*\pin+4) + #define padinputenablemsk _u(0x40) + #define padoutuptdisablemsk _u(0x80) + // First load in padbank io reg and replace bits in it. + ldr r3, =PADS_BANK0_BASE + ldr r0, [r3, PADSBNK_IOPIN_OFFSET] + bic r0, r0, (padinputenablemsk|padoutuptdisablemsk) // r0 &^= mask + orr r0, r0, padinputenablemsk + str r0, [r3, PADSBNK_IOPIN_OFFSET] + // Set the pin function in IO_BANK0. + ldr r3, =IOCTLREG + ldr r0, =\pinfn + str r0, [r3] + // Clear pinstate. + ldr r3, =USESIO + ldr r0, =pinmask + str r0, [r3, SIO_GPIOOECLR_OFFSET] // GPIO Output enable + str r0, [r3, SIO_GPIOOUTCLR_OFFSET] // Set pin to low. + .if \pinfn == pinfnSIO + str r0, [r3, SIO_GPIOOESET_OFFSET] // Assume pin is output: enable output. + .endif +.endm + +.macro pinout_init pin + pin_init \pin, pinfnSIO +.endm + +.macro pin_set pin, value + #define pinmsk _u(1<<\pin) + ldr r1, =pinmsk + ldr r0, =USESIO + .if \value == 0 + str r1, [r0, SIO_GPIOOUTCLR_OFFSET] + .else + str r1, [r0, SIO_GPIOOUTSET_OFFSET] + .endif +.endm + +.macro uart_init txpin + #define RESET_UART0 _u(0x4000000) + ldr r3, =RESETS_BASE + ldr r0, [r3] // Load value @r3 into r0. + orr r0, r0, #RESET_UART0 // Set reset bit for UART0. + str r0, [r3] // Storing the reset bit resets UART. + bic r0, r0, #RESET_UART0 // Unset the reset bit + str r0, [r3] // Clear the UART0 reset bit. + wait_bitclr r3, RESET_UART0 // Wait until peripheral is fully reset. + + // baud supported between ~200..6452000 + #define baud 115200 + #define div (8*125000000/baud) + #define ibrd _u(div>>7) + #define fbrd _u(((div&0x7f)+1)/2) + #define UART_IBRD_OFFSET _u(0x24) + #define UART_FBRD_OFFSET _u(0x28) + #define UART_LCRH_OFFSET _u(0x2C) + ldr r3, =UART0_BASE + // Start setting baud. + store_off_r3 ibrd, UART_IBRD_OFFSET + store_off_r3 fbrd, UART_FBRD_OFFSET + ldr r0, [r3, UART_LCRH_OFFSET] // Needs dummy write with contents in LCR. + str r0, [r3, UART_LCRH_OFFSET] // write back what we read. + #define uartenable (0x1) + #define uarttxenable (0x100) + #define UARTSETTINGS _u(uartenable|uarttxenable) + #define UART_CR_OFFSET _u(0x30) + ldr r0, [r3, UART_CR_OFFSET] + ldr r1, =UARTSETTINGS + orr r0, r0, r1 // Cannot use UARTSETTINGS as literal here, exceeds size of 255. + str r0, [r3, UART_CR_OFFSET] + // Configure pin 0 as UART (is Tx pin). + pin_init \txpin, pinfnUART +.endm + +.macro uart_write value + #define UART_FR_OFFSET _u(0x18) + ldr r0, =\value // Mark as immediate value, needs to be constant at compile time. + ldr r1, =UART0_BASE + str r0, [r1, UART_FR_OFFSET] // Write value in r0 to tx fifo. +.endm + +// ---------------------------------------------------------------------------- +// Start of 2nd Stage Boot Code +// ---------------------------------------------------------------------------- + +pico_default_asm_setup + +// On RP2350 boot stage2 is always called as a regular function, and should return normally +regular_func _stage2_boot +.ifdef __riscv + mv t0, ra + li a3, XIP_QMI_BASE + li a0, INIT_M0_TIMING + sw a0, QMI_M0_TIMING_OFFSET(a3) + li a0, INIT_M0_RCMD + sw a0, QMI_M0_RCMD_OFFSET(a3) + li a0, INIT_M0_RFMT + sw a0, QMI_M0_RFMT_OFFSET(a3) + // Exit routine. + jr t0 +.else + // Save link register for exit. lr hold the return address when a function call is made (bl or blx). + // We save it because we entered this bootload from the boot ROM, thus lr will contain the flash address + // immediately after this second-stage bootloader code. If bootloader is called by user lr will contain return address + // to resume execution after the bootloader ends. + // The bootloader uses r0..r1 as scratch and r3 holds the current register-of-interest base address. No other GPRs are used. + push {lr} + #define LED _u(25) + #define txpin _u(0) + // pinout_init LED + // pin_set LED, 1 + ldr r3, =XIP_QMI_BASE + // Note: STORE_OFF_R3 steps on r0. + // Here we configure/initialize QMI. + store_off_r3 INIT_M0_TIMING, QMI_M0_TIMING_OFFSET + store_off_r3 INIT_M0_RCMD, QMI_M0_RCMD_OFFSET + store_off_r3 INIT_M0_RFMT, QMI_M0_RFMT_OFFSET + // We pop the link value we saved in push {lr} above and set the program counter to it. This effectively returns from the function + // and returns to the calling function, this is because loading into pc causes the execution to jump to that address. + pop {pc} + // Declares the local symbol literals as a global symbol, making it accessible to the linker and other object files. + // ltorg instructs assembler to emit the literal pool at this point in the code. + // Places all pending literals (constants that cannot be encoded directly into instructions) at this point. // + // This ensures any ldr pseudo-instructions that reference literals have the corresponding data placed here. + // For example, when using the Thumb1 instruction set (16bit encoding) the ldr instruction offset range is limited to 0 to 1020 bytes (increments of 4 bytes), + // this is because it encodes offset as an 8 bit integer. If using Thumb2 (32bit) offset has range 0..4095, with 1 byte increment. + // + // We need to specify where we place the literals since we also need to have tight control of our bootloader size, limited to 256 bytes including 4 byte checksum. + // At the time of writing this bootloader literals section is empty, all instructions are fully encoded above. + .global literals + literals: + .ltorg +.endif + diff --git a/tools/gen-device-svd/gen-device-svd.go b/tools/gen-device-svd/gen-device-svd.go index 0c49986ab9..ed19978305 100755 --- a/tools/gen-device-svd/gen-device-svd.go +++ b/tools/gen-device-svd/gen-device-svd.go @@ -902,8 +902,9 @@ func writeGo(outdir string, device *Device, interruptSystem string) error { //go:build {{.pkgName}} && {{.device.Metadata.NameLower}} -// {{.device.Metadata.Description}} -// +/* +{{.device.Metadata.Description}} +*/ {{.device.Metadata.LicenseBlock}} package {{.pkgName}}