mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-09-01 10:19:01 +00:00
Puya PY32F MCU support (#5106)
* pinout yamls removed * machine: implement default UART pin configuration for Embedfire boards * flash command moved to chip level so VS Code plugin can select bare chip * machine: add support for alternate pin mode configuration * add build targets for embedfire on py32 * add support for embedfire target in GNUmakefile for py32 * lib/py32-svd: remove duplicate DBGMCU.IDCODE CODE field Update submodule to fix duplicate SetIDCODE/GetIDCODE method declarations in the generated py32f002bxx.go device file. * update subproject commit reference in py32-svd * refactor: update CPU frequency handling in machine and runtime packages * refactor: replace ConfigureUARTPin function with direct pin configuration in UART setup * SetAltFunc documentation for GPIO pin alternate functions * refactor: improve UART write and flush error handling with timeout * machine/py32: generalize UART driver to any USART Add a per-instance setup func to the UART type so the driver is no longer hardwired to USART1. DefaultUART stays USART1; add UART2 (USART2) in a separately build-tagged file since py32f002x parts lack USART2. RX IRQ handlers reference runtime-assigned vars to break the init cycle, and setup is assigned in the var initializer so it runs before InitSerial. Add the py32f003_32k_4k target (32K flash / 4K RAM). * Add canonical PY32F002, F003, and F030 density targets Use CMSIS/pyocd device names for target files and build tags. Add all F003 and F030 densities plus F002A/B, correct F002B to 24K flash and its own startup file, fix the F030x8 pyocd target, and update Embedfire inheritance. * Add targets for all PY32 CMSIS devices * Support all PY32 register layout variants * machine/py32: use generated register definitions * machine/py32: fix alternate-function documentation * machine/py32: report the configured CPU frequency * machine/py32: use unsafe.Add for GPIO ports * machine/py32: bound and yield UART polling * machine/py32: configure UART pins from UARTConfig * build: use the current PY32 SVD repository URL * test: cover PY32 UART register layouts * machine/py32: restore dynamic CPU frequency tracking * machine/py32: share the USART TX-ready bit * targets/py32: scale system stacks with RAM * machine/py32: keep clock state internal * targets/py32: normalize generated metadata * machine/py32: normalize GPIO configuration * machine/py32: fix T020 UART setup * machine/py32: reduce clock variant files * machine/py32: clean up UART variants * machine/py32: decouple UART clock capability * lib/py32-svd: use organization repository * runtime/py32: name 24MHz HSI encodings * machine/py32: derive startup clock from capability * machine/py32: use generated USART clock mask * machine/py32: name T020 8-bit UART mode * ci: include PY32 in sharded smoke tests * runtime: remove PY32 HSI frequency workaround
This commit is contained in:
@@ -0,0 +1,254 @@
|
||||
// Command gen-py32-targets generates PY32 target and linker definitions from
|
||||
// normalized CMSIS-Pack device metadata.
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type device struct {
|
||||
Part string
|
||||
Family string
|
||||
Core string
|
||||
FlashStart uint64
|
||||
FlashSize uint64
|
||||
RAMStart uint64
|
||||
RAMSize uint64
|
||||
}
|
||||
|
||||
type target struct {
|
||||
Inherits []string `json:"inherits"`
|
||||
BuildTags []string `json:"build-tags,omitempty"`
|
||||
LinkerScript string `json:"linkerscript,omitempty"`
|
||||
ExtraFiles []string `json:"extra-files,omitempty"`
|
||||
FlashCommand string `json:"flash-command,omitempty"`
|
||||
}
|
||||
|
||||
var orphanFamilies = map[string]string{
|
||||
// This SVD is present in PY32F0xx_DFP, but that pack has no matching PDSC
|
||||
// device entry from which a concrete memory configuration can be derived.
|
||||
"py32f001xx": "m0",
|
||||
}
|
||||
|
||||
var noGPIOAFRH = map[string]bool{
|
||||
"py32f001cxx": true,
|
||||
"py32f002bxx": true,
|
||||
"py32f002cxx": true,
|
||||
"py32f002xxx": true,
|
||||
"py32f002zxx": true,
|
||||
"py32l020xx": true,
|
||||
"py32m010xx": true,
|
||||
}
|
||||
|
||||
var gpioOSPDDER = familySet("py32e407xx", "py32f410xx", "py32l090xx", "py32t090xx", "py32t092xx")
|
||||
var gpioClockAHB2 = familySet("py32e407xx", "py32f403xx")
|
||||
var gpioClockAHB = familySet("py32f410xx")
|
||||
var hsiFSOP = familySet("py32l090xx", "py32t090xx", "py32t092xx")
|
||||
var noHSIFS = familySet("py32e407xx", "py32f403xx", "py32f410xx")
|
||||
var uartType = familySet("py32t020xx")
|
||||
var usartSplitData = familySet("py32e407xx")
|
||||
var usartUnnumbered = familySet("py32f410xx")
|
||||
var usart1ClockLiteral = familySet("py32f001cxx", "py32f001xx")
|
||||
var uartClockAPB2 = familySet("py32e407xx", "py32f403xx", "py32f410xx")
|
||||
var uartNoInterrupt = familySet("py32f410xx")
|
||||
|
||||
func familySet(names ...string) map[string]bool {
|
||||
set := make(map[string]bool, len(names))
|
||||
for _, name := range names {
|
||||
set[name] = true
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
func familyBuildTags(family string) []string {
|
||||
tags := []string{family}
|
||||
features := []struct {
|
||||
name string
|
||||
set map[string]bool
|
||||
}{
|
||||
{"py32_no_gpio_afrh", noGPIOAFRH},
|
||||
{"py32_gpio_ospdder", gpioOSPDDER},
|
||||
{"py32_gpio_clock_ahb2", gpioClockAHB2},
|
||||
{"py32_gpio_clock_ahb", gpioClockAHB},
|
||||
{"py32_hsi_fs_op", hsiFSOP},
|
||||
{"py32_no_hsi_fs", noHSIFS},
|
||||
{"py32_uart_type", uartType},
|
||||
{"py32_usart_split_data", usartSplitData},
|
||||
{"py32_usart_unnumbered", usartUnnumbered},
|
||||
{"py32_usart1_clock_literal", usart1ClockLiteral},
|
||||
{"py32_uart_clock_apb2", uartClockAPB2},
|
||||
{"py32_uart_no_interrupt", uartNoInterrupt},
|
||||
}
|
||||
for _, feature := range features {
|
||||
if feature.set[family] {
|
||||
tags = append(tags, feature.name)
|
||||
}
|
||||
}
|
||||
return tags
|
||||
}
|
||||
|
||||
var flashCommands = map[string]string{
|
||||
"py32f002ax5": "pyocd load -t py32f002ax5 {bin}",
|
||||
"py32f002bx5": "pyocd load -t py32f002bx5 {bin}",
|
||||
"py32f003x4": "pyocd load -t py32f003x4 {bin}",
|
||||
"py32f003x6": "pyocd load -t py32f003x6 {bin}",
|
||||
"py32f003x7": "pyocd load -t py32f003x7 {bin}",
|
||||
"py32f003x8": "pyocd load -t py32f003x8 {bin}",
|
||||
"py32f030x4": "pyocd load -t py32f030x4 {bin}",
|
||||
"py32f030x6": "pyocd load -t py32f030x6 {bin}",
|
||||
"py32f030x7": "pyocd load -t py32f030x7 {bin}",
|
||||
"py32f030x8": "pyocd load -t py32f030x8 {bin}",
|
||||
}
|
||||
|
||||
func main() {
|
||||
table := flag.String("table", "tools/gen-py32-targets/devices.csv", "normalized device table")
|
||||
out := flag.String("out", "targets", "target output directory")
|
||||
flag.Parse()
|
||||
|
||||
devices, err := readDevices(*table)
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
if err := generate(*out, devices); err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func fatal(err error) {
|
||||
fmt.Fprintln(os.Stderr, "gen-py32-targets:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func readDevices(path string) ([]device, error) {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
reader := csv.NewReader(file)
|
||||
header, err := reader.Read()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
wantHeader := []string{"part", "family", "core", "flash_start", "flash_size", "ram_start", "ram_size"}
|
||||
if strings.Join(header, ",") != strings.Join(wantHeader, ",") {
|
||||
return nil, fmt.Errorf("unexpected CSV header %q", strings.Join(header, ","))
|
||||
}
|
||||
|
||||
var devices []device
|
||||
seen := make(map[string]bool)
|
||||
for line := 2; ; line++ {
|
||||
record, err := reader.Read()
|
||||
if errors.Is(err, io.EOF) {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("line %d: %w", line, err)
|
||||
}
|
||||
if seen[record[0]] {
|
||||
return nil, fmt.Errorf("line %d: duplicate part %q", line, record[0])
|
||||
}
|
||||
seen[record[0]] = true
|
||||
if record[2] != "m0" && record[2] != "m4" {
|
||||
return nil, fmt.Errorf("line %d: unsupported core %q", line, record[2])
|
||||
}
|
||||
values := make([]uint64, 4)
|
||||
for i, field := range record[3:] {
|
||||
values[i], err = strconv.ParseUint(field, 0, 64)
|
||||
if err != nil || values[i] == 0 {
|
||||
return nil, fmt.Errorf("line %d: invalid memory value %q", line, field)
|
||||
}
|
||||
}
|
||||
devices = append(devices, device{
|
||||
Part: record[0], Family: record[1], Core: record[2],
|
||||
FlashStart: values[0], FlashSize: values[1],
|
||||
RAMStart: values[2], RAMSize: values[3],
|
||||
})
|
||||
}
|
||||
return devices, nil
|
||||
}
|
||||
|
||||
func systemStackSize(ramSize uint64) uint64 {
|
||||
switch {
|
||||
case ramSize <= 4*1024:
|
||||
return 1024
|
||||
case ramSize <= 16*1024:
|
||||
return 2 * 1024
|
||||
default:
|
||||
return 4 * 1024
|
||||
}
|
||||
}
|
||||
|
||||
func generate(out string, devices []device) error {
|
||||
if err := os.MkdirAll(out, 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
families := make(map[string]string, len(orphanFamilies))
|
||||
for name, core := range orphanFamilies {
|
||||
families[name] = core
|
||||
}
|
||||
for _, device := range devices {
|
||||
if core, ok := families[device.Family]; ok && core != device.Core {
|
||||
return fmt.Errorf("family %q mixes %s and %s cores", device.Family, core, device.Core)
|
||||
}
|
||||
families[device.Family] = device.Core
|
||||
}
|
||||
|
||||
familyNames := make([]string, 0, len(families))
|
||||
for name := range families {
|
||||
familyNames = append(familyNames, name)
|
||||
}
|
||||
sort.Strings(familyNames)
|
||||
for _, family := range familyNames {
|
||||
base := "py32"
|
||||
if families[family] == "m4" {
|
||||
base = "py32-m4"
|
||||
}
|
||||
spec := target{
|
||||
Inherits: []string{base},
|
||||
BuildTags: familyBuildTags(family),
|
||||
ExtraFiles: []string{"src/device/py32/" + family + ".s"},
|
||||
}
|
||||
if err := writeJSON(filepath.Join(out, family+".json"), spec); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, device := range devices {
|
||||
spec := target{
|
||||
Inherits: []string{device.Family},
|
||||
BuildTags: []string{device.Part},
|
||||
LinkerScript: "targets/" + device.Part + ".ld",
|
||||
FlashCommand: flashCommands[device.Part],
|
||||
}
|
||||
if err := writeJSON(filepath.Join(out, device.Part+".json"), spec); err != nil {
|
||||
return err
|
||||
}
|
||||
stackSize := systemStackSize(device.RAMSize)
|
||||
linker := fmt.Sprintf("MEMORY\n{\n FLASH_TEXT (rx) : ORIGIN = %#x, LENGTH = %#x\n RAM (xrw) : ORIGIN = %#x, LENGTH = %#x\n}\n\n_stack_size = %dK;\n\nINCLUDE \"targets/arm.ld\"\n", device.FlashStart, device.FlashSize, device.RAMStart, device.RAMSize, stackSize/1024)
|
||||
if err := os.WriteFile(filepath.Join(out, device.Part+".ld"), []byte(linker), 0o644); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeJSON(path string, value any) error {
|
||||
data, err := json.MarshalIndent(value, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data = append(data, '\n')
|
||||
return os.WriteFile(path, data, 0o644)
|
||||
}
|
||||
Reference in New Issue
Block a user