Sunday, February 22, 2026

Port MicroPython to PolarFire SoC

 We have built MicroPython for STM32F429DISC previously.  We'll use it as a guide to port MicroPython to MPFS Discovery Kit and Beagle-V Fire, featuring PolarFire SoC.  It is straightforward to compile for the minimal configuration.  We aim to achieve the same functionalities as STM32F429DISC.  In the hardware-wise comparison, the shortage of non-volatile memory can be overcome by using a micro-SD card.  PolarFire RISC V supports both single and double precision floating point; STM32F429 supports only single precision. 


STM32F429I-DISC1MPFS-DISCO-KIT
CPUCortex-M4FQuad RV64GC
Flash2M128K
SRAM256K1.8M
SDRAM8M1G
Debug/ProgST-LINKFlashPro
USB-UART13
Ext Header2x 2x32RPi + MikroBus
User LEDs28
Push Buttons22
Dip Switches    -8
SensorMEMS gyro-
USBOTG-
Ethernet-1 GigE
Display2.4" QVGA LCD touch-
Storage-microSD

The STM32F429DISC build includes the following modules.

>>> help('modules')
__main__          builtins          json              select
_asyncio          cmath             machine           socket
_onewire          collections       math              stm
array             deflate           micropython       struct
asyncio/__init__  dht               network           sys
asyncio/core      errno             onewire           time
asyncio/event     framebuf          os                uasyncio
asyncio/funcs     gc                platform          uctypes
asyncio/lock      hashlib           pyb               vfs
asyncio/stream    heapq             random
binascii          io                re
Plus any modules on the filesystem

The MicroPython does not seem have a good way to manage configurations.  The #define parameters that affect the build can come from a number of places, many include files and many makefiles.  And the dependencies are not always clear.

First MICROPY_CONFIG_ROM_LEVEL can range from minimum, core, basic, extra to full, which enables more feature.  We can change it from minimum to extra.  This enables the majority of features, including compiler, stack check, helper REPL, EMACS keys in REPL, scheduler, micropython module, etc.  

 Then we add the additional options enabled by STM32.  We include FROZEN_MANIFEST, which requies in the micropython-lib submodule.  We enable VFS, which turns out have some tricky dependencies.  We have to define MICROPY_VFS_FAT in the makefile in order to bring in the oofatfs.  This compiles to about 300K (text+data).  STM32 uses a portion (112K out of 2M) of its Flash to make a file system that stores initialization files like "boot.py" and "main.py". We will make use of the micro SD card.

 Next, we want to enable floating number.  We run it on one of the application cores that support floating number.  We set MICROPY_FLOAT_IMPL to double in the makefile.  This causes additional variable to be set.  This compiles to 360K and the math module is available.

We enable the machine module by defining MICRO_PY_MACHINE.  The machine module supports peripherals such as uart, spi, i2c, timer etc; board specific code has to be write to make them work. Additional python modules can be added to manifest.py.  We also include the network module.  Another useful module in STM32 is stm, which provides access to peripheral registers and we implement a similar module. The compiles to 430K.

At this point, we have the following modules,

>>> help('modules')
__main__          builtins          json              select
_asyncio          cmath             machine           socket
array             collections       math              struct
asyncio/__init__  deflate           micropython       sys
asyncio/core      errno             mpfs              time
asyncio/event     framebuf          network           uasyncio
asyncio/funcs     gc                os                uctypes
asyncio/lock      hashlib           platform          vfs
asyncio/stream    heapq             random
binascii          io                re

Now we have to write some actual code.  As a warm-up, we first add the LED class to the machine module.  Extra classes can be added by defining MACHINE_EXTRA_GLOBALS in the port modmachine.c, which is inserted into extmod/modmachine.c.  STM32 has a separate module, pyb, that contains the LED and other devices with some overlap with machine.  We can borrow code from STM32 and only change the GPIO code.  There are 8 user LEDs that can now be controlled by from Python.

Before we add the drivers for the other peripherals, GPIO, SPI, I2C, UART, Timers, we want to look into the Micropython compilation and assembly code for RISC-V by defining EMIT_RV32 and EMIT_INLINE_RV32.  MicroPython can emit native code instead of byte code.  The decorator @micropython.asm_rv32 seems to work; as example,

>>> @micropython.asm_rv32
... def mul(a0,a1):
...     mv(t0,a0)
...     mv(t1,a1)
...     mul(a0,t0,t1)
...
>>> mul(5,10)
50

But @micropython.native does not. It crashes when running the function with that decorator; the cause of the crash is illegal instruction.  Interestingly, it does not work on STM32 either.   EMIT_THUMB_ARMV7M is enabled by default, but disabling it does not solve the problem.  We'll have to investigate further.  In addition, we need to extend to support RV64.

Thursday, February 12, 2026

Sinusoidal Oscillator on PSoC

Sinusoidal signal can be generated with the wave DAC and low pass filter.  But here we will attempt to generate sine wave the analog way.  We will use a servo loop to stabilize the adjustable amplitude.

As a warm-up, we will try the simple classic Wien bridge oscillator using one PSoC opamp and a miniature light bulb.  The bulb has a resistance of 67 Ohms cold and 170 Ohms at 6mA.  It produces a nice sine signal.  




Friday, February 6, 2026

Troubleshooting a Quartz Clock

 A twenty-five year old quartz clock stopped working.  I'm curious to see what has failed.   Opening up the clock, we see a simple one-layer PCB with a COB (chip-on-board), a crystal, a transistor and a magnetic buzzer.  The clock movement comes from the stepper motor with a coil resistance of 545Ω; the coil is driven in the alternative directions every second.  A number gears form the hour, minute and second hand.  The alarm is set with another gear that causes a spring loaded contact to short out two pads when the hour hand coincides with the alarm hand and as the hour hand moves it pushes the contact away from the pads to stop the buzzer.

The clock was not completely dead.  Occasionally, it started, but it would soon stop moving.  All the electrical connections seemed OK.  To be sure of no cold soldering joints, I reflowed all the joints.  I probed all the connections that were possible to get to and it appeared that the voltages were getting to the circuit; both the stepper coil terminals sat at the rail and there were bias voltages on the quartz crystal terminals.  I could not see any obvious wrong.  During the course of troubleshooting, despair set in the first time.  Where should I go from here?

We stepped back and asked, what could possibly go wrong for 25-year old device?  Nothing on the electronics side: the COB was unlikely, the crystal might age and be out of frequency spec but not dead, and the buzzer was irrelevant.  It was more likely the wear-and-tear of the mechanical parts. 

Now I started to take apart of more the mechanical parts.  The gears seemed free to move, so I should see some movement if the electronics were driving it.  I could eventually rule out mechanical issues by noting that some voltages for the alarm were not at the correct values.  The voltages at the crystal leads would be stuck at a higher values and the voltages to the motor coil was static when compared with when it was running (the pulse to the motor was brief but still visibly to the multimeter). It implied the clock was not oscillating and the circuit did not start.  I even replaced the crystal with another one that came from a digital watch, but it did not work.  It was progress but still frustrating.

After somewhat aimlessly and repeatedly probing, I noticed that it could consistently start when the multimeter leads were placed between one of the crystal lead and the negative terminal of the battery.  I could observe the voltage decreasing from around 1V to 0.7V.  Curiously, on the PCB, there is an unpopulated footprint between the crystal lead and the negative terminal of the battery.  So perhaps placing a 10MΩ resistor and/or some capacitor there might work.  And putting the leads on the other crystal terminal stopped the clock.  However, after started, the clock would stop after a short time and leaving the probes on the circuit would not keep it running.

But why would it need an extra capacitor now?  Is aging a factor?  Has the oscillator circuit deteriorated?  Accumulation of dust or FOD causes leakage or stray capacitance?   Here we went back to the fundamentals: how does a crystal clock work?  Quartz is a pure SiO2 crystal with the piezoelectric property, i.e. an electric field physically deforms the crystal structure and vice versa.  There is a restoring force that would result in vibration.  

So dust and FOD are a strong hypothesis.  A thorough cleaning of the PCB with 99% isopropyl alcohol miraculously worked; now it could consistently start.

Analog Ground and Digital Ground

 I think this concept of analog ground and digital ground trips every young engineer.  The school did not teach me about this when going through the standard EE curriculum. You may first encounter this when using analog-to-digital converters or some microcontrollers or other mixed-signal devices.

There are plenty of application notes talking about it.  You might hear about separating analog ground from digital ground because the digital ground is noisy.  It sounds appealing.  So you draw too different ground symbols to keep them separate.  Then the question becomes where and how do you tie them together.  Some suggest tying together with an inductor which would be high impedance to AC to keep noise out.  Some say tying at the power input and others say under the ADC.  There are also ground planes, split planes.

 Why do some ICs have two or more grounds (some power devices have power grounds)?  You have to look at this from an IC designer's point of view; say you are designing a successive approximation analog to digital converter, which has a sample-and-hold, comparators, and voltage reference, and digital decoder and interface circuitry.  Every time, the digital gates switch, small pulse of current flows out of the ground node through the GND pin; if this pin is shared by all these circuit blocks, it generates a common-mode voltages (mostly from the parasitic inductance of the bond wire), which is small enough not to affect the digital circuitry but can be significant to the analog circuitry.  So here you kick the can down to road by using a separate pin and let the board design deal with it.  Now the IC designer does not mean to have the two grounds at different potentials.  So it seems that these ground pins should be tied right under the IC, which is the recommendation of many datasheets and application notes.   

Some mixed-signal devices, like analog to digital converters, should be treated more like analog devices (like opamps).  But for some devices like microcontrollers, the analog circuitry is only a small fraction of the overall device; it does not seem to make sense to be a part of the analog circuitry.

The key to the solution is to consider current flow.  When a digital gate launches an edge transition, which is incredibly fast dvdt driving mostly trace  or pin capacitance.  The return current flows with it; it tries to follow a path of least impedance, which if not properly designed may sweep through board areas with sensitive analog circuitry.