My repository: galearn
Additional data: galearn-data
Jon Nordby's repository: jonnor/galearn
Standalone tile: https://github.com/jonnor/ttsky25a-pdm-microphone
Tiny QV peripheral: https://github.com/jonnor/tinyqv-peripheral-pdm-microphone
Tiny QV on Tiny Tapeout Skywater 25 A: https://github.com/TinyTapeout/ttsky25a-tinyQV
Tiny QV on Tiny Tapeout Skywater 25 A (our fork): https://github.com/mastensg/ttsky25a-tinyQV-pdm
Tiny QV Full Peripheral Template: https://github.com/TinyTapeout/tinyqv-full-peripheral-template
Tiny QV Projects: https://github.com/MichaelBell/tinyQV-projects
Tiny QV SDK for Tiny Tapeout Skywater 25 A: https://github.com/MichaelBell/tinyQV-sdk/tree/ttsky25a
we integrated our verilog cic filter into a top level which samples the pdm microphone
does tinyqv fit on ice40hx1k? let's build it and find out.
git clone https://github.com/MichaelBell/tinyQV
./nextpnr.sh -r --up5k --json tinyqv.json --package sg48 --asc tinyqv.asc --opt-timing --pcf pico_ice.pcf Info: Device utilisation: Info: ICESTORM_LC: 2734/ 5280 51% Info: ICESTORM_RAM: 0/ 30 0% Info: SB_IO: 26/ 96 27% Info: SB_GB: 7/ 8 87% Info: ICESTORM_PLL: 0/ 1 0% Info: SB_WARMBOOT: 0/ 1 0% Info: ICESTORM_DSP: 0/ 8 0% Info: ICESTORM_HFOSC: 0/ 1 0% Info: ICESTORM_LFOSC: 0/ 1 0% Info: SB_I2C: 0/ 2 0% Info: SB_SPI: 0/ 2 0% Info: IO_I3C: 0/ 2 0% Info: SB_LEDDA_IP: 0/ 1 0% Info: SB_RGBA_DRV: 0/ 1 0% Info: ICESTORM_SPRAM: 0/ 4 0%Comparison and analysis of RISC-V processor implementations for resource-constrained FPGAs (Morawski 2024)
bin2uf2 is needed to make tinyqv.uf2.
git clone https://github.com/tinyvision-ai-inc/uf2-utils ~/uf2-utils $ m cc -Wall -Wextra -std=gnu99 -pedantic -D_XOPEN_SOURCE=700 -c libuf2.c -o libuf2.o cc -Wall -Wextra -std=gnu99 -pedantic -D_XOPEN_SOURCE=700 -c bin2uf2.c -o bin2uf2.o cc -Wall -Wextra -std=gnu99 -pedantic -D_XOPEN_SOURCE=700 -c uf22bin.c -o uf22bin.o cc -Wall -Wextra -std=gnu99 -pedantic -D_XOPEN_SOURCE=700 -c uf2dump.c -o uf2dump.o cc libuf2.o bin2uf2.o -o bin2uf2 cc libuf2.o uf22bin.o -o uf22bin cc libuf2.o uf2dump.o -o uf2dump ~/uf2-utils $ doas make install
local hardening https://tinytapeout.com/guides/local-hardening/
git clone https://github.com/jonnor/ttsky25a-pdm-microphone cd ttsky25a-pdm-microphone git clone -b ttsky25a https://github.com/TinyTapeout/tt-support-tools tt python3 -m venv venv source venv/bin/activate pip install -r tt/requirements.txt pip install https://github.com/TinyTapeout/libparse-python/releases/download/0.3.1-dev1/libparse-0.3.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl export PDK=sky130A pip install openlane==2.2.9 systemctl start docker ./tt/tt_tool.py --create-user-config time ./tt/tt_tool.py --harden time ./tt/tt_tool.py --create-png
tinyqv on pico-ice
Terminal ready pico-ice default firmware https://github.com/tinyvision-ai-inc/pico-ice/tree/main/Firmware/pico-ice-default Serial port #0 - this shell, with commands: v - print pico-ice-sdk version Serial port #1 - forwarding to UART UART TX on RP0 = ICE27 UART RX on RP1 = ICE25 Serial port #2 - forwarding to SPI: https://pico-ice.tinyvision.ai/ice_usb.html#usb-spi-fpgasramflash-forwarding pico-ice> v pico-ice-sdk v1.3.0-2-g7c5bfe7 pico-ice>https://pico-ice.tinyvision.ai/
install micropython https://www.raspberrypi.com/documentation/microcontrollers/micropython.html#drag-and-drop-micropython
MicroPython v1.25.0 on 2025-04-15; Raspberry Pi Pico with RP2040 Type "help()" for more information.
github actions succeed to a greater extent:
docs generates datasheet PDF.
gds generates render PNG.
in a textbook example of test-driven development, jon added a failing test case with cocotb driving our peripheral, and i implemented those features that were tested.
after merging both our additions, the new test passed: https://github.com/jonnor/tinyqv-peripheral-pdm-microphone/actions/runs/16655981262/job/47140950625
jon's test check that the control and clock scaling registers can be written and read back: https://github.com/jonnor/tinyqv-peripheral-pdm-microphone/commit/e9fa0fe44187089b1f1473d237fc925054f4909c
my implementation adds a clock divider, non-configurable for now, and gated clock output: https://github.com/jonnor/tinyqv-peripheral-pdm-microphone/commit/1ee28c8a358b9445d0bfa7c4374e5ce2f5c33712
yosys found errors: https://github.com/jonnor/tinyqv-peripheral-pdm-microphone/actions/runs/16657475826/job/47146013552#step:3:4217
some inputs are unused, and some outputs are not driven. i try to "use" all inputs, and drive all outputs, but yosys still complains: https://github.com/jonnor/tinyqv-peripheral-pdm-microphone/actions/runs/16669724236/job/47183063510#step:3:4183
yosys probably complained because i set the same register from two always blocks. i merged the statements into one block, and now the design hardens again: https://github.com/jonnor/tinyqv-peripheral-pdm-microphone/actions/runs/16683579187
i made the pdm bit clock output's period configurable, and tested more divisors. only even periods succeed.
sunday. i jammed our cic3 pdm filter into our 1x2 tile tinyqv peripheral. it seems to barely fit:
but now "GL" tests fail...
monday. we tried for hours to run our peripheral on the pico-ice board. jon got SPI control from RP2040 to peripheral in ICE40 working after a tip from rebelmike.
tuesday. i verified that we can configure pdm clock frequency over spi on pico-ice. with a real pdm microphone connected, i can read pcm samples from the peripheral while running. the micropython script prints the samples too slow to construct a waveform, but the numbers are higher when i make more noise.
thursday, day 0 of WHY 2025. our github actions failed because tt-support-tools required version 0.29.10 of klayout, but klayout released a new version tonight, and deleted this old version from pypi because each package is limited to 10 GB there.
friday, day 1 of WHY 2025. i am implementing interrupts.
saturday, day 2 of WHY 2025. i implemented interrupts.
monday, day 4 of WHY 2025. gds/gl_test fails: https://github.com/jonnor/tinyqv-peripheral-pdm-microphone/actions/runs/16876118264/job/47801332156
error:
ValueError: Unresolvable bit in binary string: 'x'. Set the COCOTB_RESOLVE_X environment variable to configure how special values are resolved.
tuesday, day 5 of WHY 2025. we fixed gl_test by making all signals well-defined at all times.
i forked tinyqv, and started integrating our peripheral: https://github.com/mastensg/ttsky25a-tinyQV-pdm/
wednesday, after WHY 2025. i submitted a request to merge our peripheral into tinyqv: https://github.com/TinyTapeout/ttsky25a-tinyQV/pull/27
saturday. adding our peripheral into tinyqv makes the gds action time out: https://github.com/TinyTapeout/ttsky25a-tinyQV/actions/runs/16993564404/job/48178668029?pr=27
Michael Bell comments:
I don't think there is anything wrong here, it's just adding this peripheral in makes the overall design too big/complex to fit in the current area.
Larger designs than 5x4 tiles are not yet supported, but should be added soon. Then we'll be able to increase the size and merge this in.
sunday. i attempt to test tinyqv including our peripheral on pico-ice's fpga, ice40up5. i follow this guide: https://github.com/mastensg/ttsky25a-tinyQV-pdm/tree/7f4df1dbc339b7266132f5cc2a092e3a4ed9b1b1/fpga/pico-ice
a aborted make after 32 minutes. nextpnr kept printing these errors:
# Place and route using nextpnr ./nextpnr.sh -r --up5k --json tinyqv.json --package sg48 --asc tinyqv.asc --opt-timing --pcf pico_ice.pcf ERROR: Max frequency for clock 'clk$SB_IO_IN_$glb_clk': 11.00 MHz (FAIL at 14.00 MHz) ERROR: Max frequency for clock 'clk$SB_IO_IN_$glb_clk': 11.59 MHz (FAIL at 14.00 MHz) ERROR: Max frequency for clock 'clk$SB_IO_IN_$glb_clk': 10.81 MHz (FAIL at 14.00 MHz) ERROR: Max frequency for clock 'clk$SB_IO_IN_$glb_clk': 11.12 MHz (FAIL at 14.00 MHz) ERROR: Max frequency for clock 'clk$SB_IO_IN_$glb_clk': 11.44 MHz (FAIL at 14.00 MHz) ERROR: Max frequency for clock 'clk$SB_IO_IN_$glb_clk': 11.43 MHz (FAIL at 14.00 MHz)
i disabled some user peripherals, which fixed the issue:
diff --git a/fpga/pico-ice/peripherals.v b/fpga/pico-ice/peripherals.v index 5c97665..43ba99f 100644 --- a/fpga/pico-ice/peripherals.v +++ b/fpga/pico-ice/peripherals.v @@ -247,7 +247,7 @@ module tinyQV_peripherals #(parameter CLOCK_MHZ=64) ( .user_interrupt(user_interrupts[6]) ); - tqvp_toivoh_pwl_synth i_user_peri07 ( + tqvp_full_empty i_user_peri07 ( .clk(clk), .rst_n(rst_n), @@ -452,7 +452,7 @@ module tinyQV_peripherals #(parameter CLOCK_MHZ=64) ( .data_out(data_from_simple_peri[1]) ); - tqvp_cattuto_ws2812b_driver #(.CLOCK_MHZ(CLOCK_MHZ)) i_cattuto_ws2812b_driver02 ( + tqvp_byte_empty i_cattuto_ws2812b_driver02 ( .clk(clk), .rst_n(rst_n),
i run "PROG=hello make run". D3 lights up green, but i find no text on UART.
i download michaelbell's gnu toolchain release: https://github.com/MichaelBell/riscv-gnu-toolchain/releases/download/15.1.0-tqv-2.0/riscv32ec-15.1.0-tqv-2.0.tar.gz
tuesday. retry fpga/pico-ice. UART RX/TX were swapped. after swapping currectly, hello.bin prints hello, and tqv-micropython.bin presents micropython.
including our pdm peripheral barely passes at 14 MHz:
$ time make # Lint #verilator --lint-only -Wall -Wno-DECLFILENAME -Wno-MULTITOP pico_ice.v peripherals.v ../../src/tinyQV/cpu/*.v ../../src/tinyQV/peri/uart/*.v ../../src/peri_byte_example.v ../../src/peri_full_example.v ../../src/peri_full_empty.v ../../src/peri_byte_empty.v ../../src/user_peripherals/*.v ../../src/user_peripherals/*.sv ../../src/user_peripherals/*/*.v ../../src/user_peripherals/*/*.sv # synthesize using Yosys yosys -p "read -sv pico_ice.v peripherals.v ../../src/tinyQV/cpu/*.v ../../src/tinyQV/peri/uart/*.v ../../src/peri_byte_example.v ../../src/peri_full_example.v ../../src/peri_full_empty.v ../../src/peri_byte_empty.v ../../src/user_peripherals/*.v ../../src/user_peripherals/*.sv ../../src/user_peripherals/*/*.v ../../src/user_peripherals/*/*.sv ; synth_ice40 -abc9 -device u -top tinyQV_top -json tinyqv.json" -DICE40 -DPURE_RTL -DSYNTH_FPGA > yosys.log Warning: Yosys has only limited support for tri-state logic at the moment. (../../src/user_peripherals/pwl_synth/pwl_synth.sv:2361) Warning: Replacing memory \gpio_out_func_sel with list of registers. See peripherals.v:143 Warning: Replacing memory \gpio_out_func_sel with list of registers. See peripherals.v:143 Warning: Replacing memory \registers with list of registers. See ../../src/tinyQV/cpu/register.v:44 ABC: Warning: AIG with boxes has internal fanout in 0 complex flops and 2 carries. ABC: Warning: The network is combinational. ABC: Warning: AIG with boxes has internal fanout in 0 complex flops and 2 carries. ABC: Warning: AIG with boxes has internal fanout in 0 complex flops and 2 carries. Warnings: 3 unique messages, 4 total Number of cells: 4653 SB_DFF* 1563 SB_LUT4 2635 # Place and route using nextpnr ./nextpnr.sh -r --up5k --json tinyqv.json --package sg48 --asc tinyqv.asc --opt-timing --pcf pico_ice.pcf ERROR: Max frequency for clock 'clk$SB_IO_IN_$glb_clk': 13.78 MHz (FAIL at 14.00 MHz) ERROR: Max frequency for clock 'clk$SB_IO_IN_$glb_clk': 13.97 MHz (FAIL at 14.00 MHz) ERROR: Max frequency for clock 'clk$SB_IO_IN_$glb_clk': 13.08 MHz (FAIL at 14.00 MHz) Info: Max frequency for clock 'clk$SB_IO_IN_$glb_clk': 14.71 MHz (PASS at 14.00 MHz) # Convert to bitstream using IcePack icepack tinyqv.asc tinyqv.bin real 1m52.831s user 1m56.057s sys 0m0.736s
i fail to set clock rate and enable generation:
>>> machine.mem32[0x800_0100 + 64*10 + 0] 0 >>> machine.mem32[0x800_0100 + 64*10 + 4] 0 >>> machine.mem32[0x800_0100 + 64*10 + 4] = 14 >>> machine.mem32[0x800_0100 + 64*10 + 4] 0 >>> machine.mem32[0x800_0100 + 64*10 + 4] 0 >>> machine.mem32[0x800_0100 + 64*10 + 4] = 14 >>> machine.mem32[0x800_0100 + 64*10 + 4] 0 >>> machine.mem32[0x800_0100 + 64*10 + 8] 0 >>> machine.mem32[0x800_0100 + 64*10 + 0] = 1 >>> machine.mem32[0x800_0100 + 64*10 + 8] 0 >>> machine.mem32[0x800_0100 + 64*10 + 0] 0 >>>
wednesday. still try to get a response from our pdm peripheral on pico-ice.
>>> MicroPython v1.25.0-9.g62a506733.dirty on 2025-08-08; ttsky25a with tinyQV Type "help()" for more information. >>> import machine >>> machine.mem32[0x800_03fc] 0
GPIO, pin 31 = ui_in[3] forced low:
>>> bin(machine.mem8[0x08000044]) '0b11110111'
make pdm return 0xAAAAAAAA for all reads, then scan all peripherals:
for i in range(0x0800_0100, 0x0800_0400, 4): print(hex(i), hex(machine.mem32[i])) [...] 0x8000264 0x0 0x8000268 0x0 0x800026c 0x0 0x8000270 0x0 0x8000274 0x0 0x8000278 0x0 0x800027c 0x0 0x8000280 -0x55555556 0x8000284 -0x55555556 0x8000288 -0x55555556 0x800028c -0x55555556 0x8000290 -0x55555556 0x8000294 -0x55555556 0x8000298 -0x55555556 0x800029c -0x55555556 0x80002a0 -0x55555556 0x80002a4 -0x55555556 0x80002a8 -0x55555556 0x80002ac -0x55555556 0x80002b0 -0x55555556 0x80002b4 -0x55555556 0x80002b8 -0x55555556 0x80002bc -0x55555556 0x80002c0 0x0 0x80002c4 0x0 0x80002c8 0x0 0x80002cc 0x0 0x80002d0 0x0 0x80002d4 0x0 0x80002d8 0x0 0x80002dc 0x0 0x80002e0 0x0 0x80002e4 0x0 0x80002e8 0x0 [...]
i was off by 0x100.
RebelMike helpfully linked us his peripheral spreadsheet: https://docs.google.com/spreadsheets/d/1ru2YCSY_XCv5kk4FDyfl4eAzEzy9SP3UOAXtZt6OAMY/edit?gid=719809069#gid=719809069
works:
>>> MicroPython v1.25.0-9.g62a506733.dirty on 2025-08-08; ttsky25a with tinyQV Type "help()" for more information. >>> import machine >>> machine.mem32[0x8000284] 0 >>> machine.mem32[0x8000284] = 14 >>> machine.mem32[0x8000284] 14
FUNC_SEL to enable peripheral function? https://github.com/TinyTapeout/ttsky25a-tinyQV/blob/main/docs/info.md#gpio
1 MHz clock output:
>>> import machine >>> out6 = machine.Pin(6, func_sel=10) >>> machine.mem32[0x8000284]=14 >>> machine.mem32[0x8000280]=1
thursday. set up pico-ice environment on new computer:
doas apt install arachne-pnr micropython-mpremote nextpnr-ice40 verilator yosys mkdir ~/tiny cd ~/tiny git clone git@github.com:mastensg/ttsky25a-tinyQV-pdm.git cd ttsky25a-tinyQV-pdm git switch pdm git submodule update --init cd fpga/pico-ice make make burn make run make reset cd ~/tiny git clone git@github.com:MichaelBell/tinyQV-sdk.git cd tinyQV-sdk git switch ttsky25a make doas mkdir /opt/tinyQV doas chown `id -u`:`id -g` /opt/tinyQV cd ~/tiny wget https://github.com/MichaelBell/riscv-gnu-toolchain/releases/download/15.1.0-tqv-2.0/riscv32ec-15.1.0-tqv-2.0.tar.gz cd /opt tar xf ~/tiny/riscv32ec-15.1.0-tqv-2.0.tar.gz # git clone git@github.com:MichaelBell/riscv-gnu-toolchain.git # cd riscv-gnu-toolchain # ./configure --prefix=/opt/tinyQV --with-arch=rv32ec_zcb_zicond --with-abi=ilp32e # make
pdm_test program:
cd ~/tiny/ttsky25a-tinyQV-pdm git switch test-pdm cd fpga/pico-ice/pdm_test make
much hacking happened, and then i got this recording: galearn-tinyqv-pdm-tty.txt.wav.plus30db.ogg