Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions src/machine/machine_atsamd21_simulator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//go:build !baremetal && (gemma_m0 || qtpy || trinket_m0 || arduino_mkr1000 || arduino_mkrwifi1010 || arduino_nano33 || arduino_zero || circuitplay_express || feather_m0_express || feather_m0 || itsybitsy_m0 || p1am_100 || xiao)

// Simulated atsamd21 chips.

package machine

// The timer channels/pins match the hardware, and encode the same information
// as pinTimerMapping but in a more generic (less efficient) way.

var TCC0 = timerType{
instance: 0,
frequency: 48e6,
bits: 24,
prescalers: []int{1, 2, 4, 8, 16, 64, 256, 1024},
channelPins: [][]Pin{
{PA04, PA08, PB10, PA14, PB16, PA22, PB30}, // channel 0
{PA05, PA09, PB11, PA15, PB17, PA23, PB31}, // channel 1
{PA10, PB12, PA12, PA16, PA18, PA20}, // channel 2
{PA11, PB13, PA13, PA17, PA19, PA21}, // channel 3
},
}

var TCC1 = timerType{
instance: 1,
frequency: 48e6,
bits: 24,
prescalers: []int{1, 2, 4, 8, 16, 64, 256, 1024},
channelPins: [][]Pin{
{PA06, PA10, PA30}, // channel 0
{PA07, PA11, PA31}, // channel 1
{PA08, PA24, PB30}, // channel 2
{PA09, PA25, PB31}, // channel 3
},
}

var TCC2 = timerType{
instance: 2,
frequency: 48e6,
bits: 16,
prescalers: []int{1, 2, 4, 8, 16, 64, 256, 1024},
channelPins: [][]Pin{
{PA00, PA12, PA16}, // channel 0
{PA01, PA13, PA17}, // channel 1
},
}
97 changes: 97 additions & 0 deletions src/machine/machine_generic.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,103 @@ func gpioSet(pin Pin, value bool)
//export __tinygo_gpio_get
func gpioGet(pin Pin) bool

// Generic PWM/timer peripheral. Properties can be configured depending on the
// hardware.
type timerType struct {
// Static properties.
instance int32
frequency uint64
bits int
prescalers []int
channelPins [][]Pin

// Configured 'top' value.
top uint32
}

// Configure the PWM/timer peripheral.
func (t *timerType) Configure(config PWMConfig) error {
// Note: for very large period values, this multiplication will overflow.
top := config.Period * t.frequency / 1e9
if config.Period == 0 {
top = 0xffff // default for LEDs
}

// The maximum value that can be stored with the given number of bits in
// this timer.
maxTop := uint64(1)<<uint64(t.bits) - 1

// Look for an appropriate prescaler value.
var prescaler int
for _, div := range t.prescalers {
if top/uint64(div) <= maxTop {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: if scaled := top/uint64(div); scaled <= maxTop { and then top = scaled below. Not sure the performance and/or the readability gain is worth it, though.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually that won't help performance at all. Compilers are very good at recognizing this sort of pattern and only doing the operation once. In LLVM, this is probably done by InstCombine.

(And in any case this code is typically run in WebAssembly in a browser, so performance is less of an issue).

prescaler = div
top = top / uint64(div)
break
}
}
if prescaler == 0 {
return ErrPWMPeriodTooLong
}

// Set these values as the configuration.
t.top = uint32(top)
pwmConfigure(t.instance, float64(t.frequency)/float64(prescaler), uint32(top))

return nil
}

// Channel returns a PWM channel for the given pin. Note that one channel may be
// shared between multiple pins, and so will have the same duty cycle. If this
// is not desirable, look for a different PWM/timer peripheral or consider using
// a different pin.
func (t *timerType) Channel(pin Pin) (uint8, error) {
for ch, pins := range t.channelPins {
// For nrf52xxx chips specifically we can assign any channel to any pin.
// We use a similar (identical?) logic to the hardware implementation,
// and pick the first empty channel.
if pins == nil {
t.channelPins[ch] = []Pin{pin}
pwmChannelConfigure(t.instance, int32(ch), pin)
return uint8(ch), nil
}

// Check whether the pin can be used on this channel.
for _, p := range pins {
if p == pin {
pwmChannelConfigure(t.instance, int32(ch), pin)
return uint8(ch), nil
}
}
}

return 0, ErrInvalidOutputPin
}

func (t *timerType) Set(channel uint8, value uint32) {
pwmChannelSet(t.instance, channel, value)
}

// Top returns the current counter top, for use in duty cycle calculation. It
// will only change with a call to Configure or SetPeriod, otherwise it is
// constant.
//
// The value returned here is hardware dependent. In general, it's best to treat
// it as an opaque value that can be divided by some number and passed to Set
// (see Set documentation for more information).
func (t *timerType) Top() uint32 {
return t.top
}

//export __tinygo_pwm_configure
func pwmConfigure(instance int32, frequency float64, top uint32)

//export __tinygo_pwm_channel_configure
func pwmChannelConfigure(instance, channel int32, pin Pin)

//export __tinygo_pwm_channel_set
func pwmChannelSet(instance int32, channel uint8, value uint32)

type SPI struct {
Bus uint8
}
Expand Down
60 changes: 60 additions & 0 deletions src/machine/machine_nrf52840_simulator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
//go:build !baremetal && (bluemicro840 || circuitplay_bluefruit || clue_alpha || feather_nrf52840_sense || feather_nrf52840 || itsybitsy_nrf52840 || mdbt50qrx || nano_33_ble || nicenano || nrf52840_mdk || particle_3rd_gen || pca10056 || pca10059 || rak4631 || reelboard || xiao_ble)

// Simulator support for nrf52840 based boards.

package machine

// Channel values below are nil, so that they get filled in on the first use.
// This is the same as what happens on baremetal.

var PWM0 = timerType{
instance: 0,
frequency: 16e6,
bits: 15,
prescalers: []int{1, 2, 4, 8, 16, 32, 64, 128},
channelPins: [][]Pin{
nil, // channel 0
nil, // channel 1
nil, // channel 2
nil, // channel 3
},
}

var PWM1 = timerType{
instance: 1,
frequency: 16e6,
bits: 15,
prescalers: []int{1, 2, 4, 8, 16, 32, 64, 128},
channelPins: [][]Pin{
nil, // channel 0
nil, // channel 1
nil, // channel 2
nil, // channel 3
},
}

var PWM2 = timerType{
instance: 2,
frequency: 16e6,
bits: 15,
prescalers: []int{1, 2, 4, 8, 16, 32, 64, 128},
channelPins: [][]Pin{
nil, // channel 0
nil, // channel 1
nil, // channel 2
nil, // channel 3
},
}

var PWM3 = timerType{
instance: 3,
frequency: 16e6,
bits: 15,
prescalers: []int{1, 2, 4, 8, 16, 32, 64, 128},
channelPins: [][]Pin{
nil, // channel 0
nil, // channel 1
nil, // channel 2
nil, // channel 3
},
}
96 changes: 96 additions & 0 deletions src/machine/machine_rp2040_simulator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
//go:build !baremetal && (ae_rp2040 || badger2040_w || badger2040 || challenger_rp2040 || elecrow_rp2040 || feather_rp2040 || gopher_badge || kb2040 || macropad_rp2040 || nano_rp2040 || pico || qtpy_rp2040 || thingplus_rp2040 || thumby || trinkey_qt2040 || tufty2040 || waveshare_rp2040_tiny || waveshare_rp2040_zero || xiao_rp2040)

// Simulator support for the RP2040.
//
// This is *only* for the RP2040. RP2350 is a different chip with slightly
// different characteristics.

package machine

var PWM0 = timerType{
instance: 0,
frequency: 200e6,
bits: 16,
prescalers: []int{1, 2, 4, 8, 16, 32, 64, 128, 256}, // actually a continuing range, TODO
channelPins: [][]Pin{
{GPIO0, GPIO16}, // channel A (0)
{GPIO1, GPIO17}, // channel B (1)
},
}

var PWM1 = timerType{
instance: 0,
frequency: 200e6,
bits: 16,
prescalers: []int{1, 2, 4, 8, 16, 32, 64, 128, 256},
channelPins: [][]Pin{
{GPIO2, GPIO18}, // channel A (0)
{GPIO3, GPIO19}, // channel B (1)
},
}

var PWM2 = timerType{
instance: 0,
frequency: 200e6,
bits: 16,
prescalers: []int{1, 2, 4, 8, 16, 32, 64, 128, 256},
channelPins: [][]Pin{
{GPIO4, GPIO20}, // channel A (0)
{GPIO5, GPIO21}, // channel B (1)
},
}

var PWM3 = timerType{
instance: 0,
frequency: 200e6,
bits: 16,
prescalers: []int{1, 2, 4, 8, 16, 32, 64, 128, 256},
channelPins: [][]Pin{
{GPIO6, GPIO22}, // channel A (0)
{GPIO7, GPIO23}, // channel B (1)
},
}

var PWM4 = timerType{
instance: 0,
frequency: 200e6,
bits: 16,
prescalers: []int{1, 2, 4, 8, 16, 32, 64, 128, 256},
channelPins: [][]Pin{
{GPIO8, GPIO24}, // channel A (0)
{GPIO9, GPIO25}, // channel B (1)
},
}

var PWM5 = timerType{
instance: 0,
frequency: 200e6,
bits: 16,
prescalers: []int{1, 2, 4, 8, 16, 32, 64, 128, 256},
channelPins: [][]Pin{
{GPIO10, GPIO26}, // channel A (0)
{GPIO11, GPIO27}, // channel B (1)
},
}

var PWM6 = timerType{
instance: 0,
frequency: 200e6,
bits: 16,
prescalers: []int{1, 2, 4, 8, 16, 32, 64, 128, 256},
channelPins: [][]Pin{
{GPIO12, GPIO28}, // channel A (0)
{GPIO13, GPIO29}, // channel B (1)
},
}

var PWM7 = timerType{
instance: 0,
frequency: 200e6,
bits: 16,
prescalers: []int{1, 2, 4, 8, 16, 32, 64, 128, 256},
channelPins: [][]Pin{
{GPIO14}, // channel A (0)
{GPIO15}, // channel B (1)
},
}