Gate Array Learn

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

Log

2025-05-14

pdm

2025-07-16

we integrated our verilog cic filter into a top level which samples the pdm microphone

2025-07-20

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

2025-07-23

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.

2025-07-28

github actions succeed to a greater extent:

docs generates datasheet PDF.

gds generates render PNG.

2025-07-31

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

2025-08-01

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.

2025-08-03

sunday. i jammed our cic3 pdm filter into our 1x2 tile tinyqv peripheral. it seems to barely fit:

pdm

but now "GL" tests fail...

2025-08-04

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.

2025-08-05

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.

2025-08-07

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.

2025-08-08

friday, day 1 of WHY 2025. i am implementing interrupts.

2025-08-09

saturday, day 2 of WHY 2025. i implemented interrupts.

2025-08-11

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.

2025-08-12

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/

2025-08-13

wednesday, after WHY 2025. i submitted a request to merge our peripheral into tinyqv: https://github.com/TinyTapeout/ttsky25a-tinyQV/pull/27

2025-08-16

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.

2025-08-31

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

2025-09-02

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
>>>

2025-09-03

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

2025-09-04

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

2025-09-05

friday, night of competition deadline.

i added ability to select which pin is used as PDM data input. at the same time, i narrowed all of our registers to contain only the bits we used, which made our design overall sparser.

2025-09-10

wednesday. we submit the tinyqv pdm peripheral in spi wrapper as standalone tile.

2025-09-11

thursday. i try to expose all 32 bits of pcm from filter over spi. it's too dense:

Given target density: 0.70                                                      
Suggested target density: 0.81

the 24 least significant bits fit, though.