diff --git a/GPIO/iCE40/.gitignore b/GPIO/iCE40/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..9bc6787c2c1112e1864425cc2fcf769159c197e3
--- /dev/null
+++ b/GPIO/iCE40/.gitignore
@@ -0,0 +1,6 @@
+/*.asc
+/*.bin
+/*.json
+/*.rpt
+/*.nplog
+/*.yslog
diff --git a/GPIO/iCE40/Makefile b/GPIO/iCE40/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..44aaad51526e1cad5a8c2cd847ba350890759163
--- /dev/null
+++ b/GPIO/iCE40/Makefile
@@ -0,0 +1,29 @@
+PROJ = ring
+
+all: $(PROJ).rpt $(PROJ).bin
+
+$(PROJ).json: $(PROJ).v
+	yosys -ql $(PROJ).yslog -p 'synth_ice40 -top top -json $@' $<
+
+$(PROJ).asc: $(PROJ).json icebreaker.pcf
+	nextpnr-ice40 -ql $(PROJ).nplog --up5k --package sg48 --freq 111 --asc $@ --pcf icebreaker.pcf --json $<
+
+$(PROJ).bin: $(PROJ).asc
+	icepack $< $@
+
+$(PROJ).rpt: $(PROJ).asc
+	icetime -d up5k -c 111 -mtr $@ $<
+
+prog: $(PROJ).bin
+	iceprog $<
+
+sudo-prog: $(PROJ).bin
+	@echo 'Executing prog as root!!!'
+	sudo iceprog $<
+
+clean:
+	rm -f $(PROJ).yslog $(PROJ).nplog $(PROJ).json $(PROJ).asc $(PROJ).rpt $(PROJ).bin
+	rm -f $(PROJ)_tb $(PROJ)_tb.vcd $(PROJ)_syn.v $(PROJ)_syntb $(PROJ)_syntb.vcd
+
+.SECONDARY:
+.PHONY: all prog clean
diff --git a/GPIO/iCE40/README.md b/GPIO/iCE40/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..08bc585256f6d90e07fb8803bbeb937d3f9f9d4c
--- /dev/null
+++ b/GPIO/iCE40/README.md
@@ -0,0 +1,41 @@
+# iCE40 Ring Test
+
+## Dependencies
+
+- [IceStorm tools](https://github.com/YosysHQ/icestorm)
+- [yosys](http://www.clifford.at/yosys/download.html)
+- [nextpnr](https://github.com/YosysHQ/nextpnr)
+
+Install instructions for all of the above tools can be found on the [IceStorm
+website](http://bygone.clairexen.net/icestorm/).
+
+## Building
+
+The default `make` target synthesizes the design and runs a timing analysis. The target `prog`
+programs an available iCEBreaker via USB.
+
+## Results
+
+When clocking the FPGA at 24MHz, the resulting ring oscillation is still identifiable as a square
+wave.
+
+![12MHz](./img/ring_12mhz.png)
+
+By 111MHz, it's more of a triangle wave. But still stable and nearly rail to rail. This is the
+fastest clock speed that `icetime` approves.
+
+![55.5MHz](./img/ring_55mhz.png)
+
+Despite not passing the timing analysis, when the FPGA is clocked at 120MHz no problems seems to
+occur.
+
+![60MHz](./img/ring_60mhz.png)
+
+Even at 129MHz, stable ring oscillation can occur. I (Erik) thought that the first time I ran it I
+got a chaotic, aperiodic signal. But I haven't been able to reproduce this so I might have made a
+mistake. Still, I'll hold off on declaring this result official.
+
+![64.5MHz](./img/ring_64mhz.png)
+
+At faster clock speeds things start to go wrong. Usually there's still a periodic ring oscillation,
+just much slower than one would hope.
diff --git a/GPIO/iCE40/icebreaker.pcf b/GPIO/iCE40/icebreaker.pcf
new file mode 100644
index 0000000000000000000000000000000000000000..03088f26b53104d494d4d1df7fac29e09164d503
--- /dev/null
+++ b/GPIO/iCE40/icebreaker.pcf
@@ -0,0 +1,54 @@
+# 12 MHz clock
+set_io -nowarn CLK_12MHZ  35
+
+# RS232
+set_io -nowarn RX          6
+set_io -nowarn TX          9
+
+# LEDs and Button
+set_io -nowarn BTN_N      10
+set_io -nowarn LEDR_N     11
+set_io -nowarn LEDG_N     37
+
+# RGB LED Driver
+set_io -nowarn LED_RED_N  39
+set_io -nowarn LED_GRN_N  40
+set_io -nowarn LED_BLU_N  41
+
+# SPI Flash
+set_io -nowarn FLASH_SCK  15
+set_io -nowarn FLASH_SSB  16
+set_io -nowarn FLASH_IO0  14
+set_io -nowarn FLASH_IO1  17
+set_io -nowarn FLASH_IO2  12
+set_io -nowarn FLASH_IO3  13
+
+# PMOD 1A
+set_io -nowarn P1A1        4
+set_io -nowarn P1A2        2
+set_io -nowarn P1A3       47
+set_io -nowarn P1A4       45
+set_io -nowarn P1A7        3
+set_io -nowarn P1A8       48
+set_io -nowarn P1A9       46
+set_io -nowarn P1A10      44
+
+# PMOD 1B
+set_io -nowarn P1B1       43
+set_io -nowarn P1B2       38
+set_io -nowarn P1B3       34
+set_io -nowarn P1B4       31
+set_io -nowarn P1B7       42
+set_io -nowarn P1B8       36
+set_io -nowarn P1B9       32
+set_io -nowarn P1B10      28
+
+# LEDs and Buttons (PMOD 2)
+set_io -nowarn LED1       26
+set_io -nowarn LED2       27
+set_io -nowarn LED3       25
+set_io -nowarn LED4       23
+set_io -nowarn LED5       21
+set_io -nowarn BTN1       20
+set_io -nowarn BTN2       19
+set_io -nowarn BTN3       18
diff --git a/GPIO/iCE40/img/ring_12mhz.png b/GPIO/iCE40/img/ring_12mhz.png
new file mode 100644
index 0000000000000000000000000000000000000000..32bfb41babac05c30731b593a01f29e03533c659
Binary files /dev/null and b/GPIO/iCE40/img/ring_12mhz.png differ
diff --git a/GPIO/iCE40/img/ring_55mhz.png b/GPIO/iCE40/img/ring_55mhz.png
new file mode 100644
index 0000000000000000000000000000000000000000..3e6bed31b2ccb922130a8d2571b6d1d03940ce37
Binary files /dev/null and b/GPIO/iCE40/img/ring_55mhz.png differ
diff --git a/GPIO/iCE40/img/ring_60mhz.png b/GPIO/iCE40/img/ring_60mhz.png
new file mode 100644
index 0000000000000000000000000000000000000000..5b8e2b9d547b618a2279b6cfa5389df5b19faaac
Binary files /dev/null and b/GPIO/iCE40/img/ring_60mhz.png differ
diff --git a/GPIO/iCE40/img/ring_64mhz.png b/GPIO/iCE40/img/ring_64mhz.png
new file mode 100644
index 0000000000000000000000000000000000000000..f91a302a7e93ff797e3b5ccdc0bc433dc69fd88d
Binary files /dev/null and b/GPIO/iCE40/img/ring_64mhz.png differ
diff --git a/GPIO/iCE40/ring.v b/GPIO/iCE40/ring.v
new file mode 100644
index 0000000000000000000000000000000000000000..4e4c830680ac0155259094d85eddec559f09119b
--- /dev/null
+++ b/GPIO/iCE40/ring.v
@@ -0,0 +1,64 @@
+// Cause yosys to throw an error when we implicitly declare nets
+`default_nettype none
+
+module top (
+    input CLK_12MHZ,           // system clock
+    output P1A1,
+    input P1A2,
+);
+    wire clk;
+    wire clk_lock;
+    SB_PLL40_PAD #(
+        .FEEDBACK_PATH("SIMPLE"),
+        .PLLOUT_SELECT("GENCLK"),
+        .FILTER_RANGE(3'b001),
+        // == 24 MHz == (boring slow)
+        //.DIVR(4'b0000),
+        //.DIVF(7'b0111111),
+        //.DIVQ(3'b101)
+        // == 48 MHz == (getting interesting)
+        //.DIVR(4'b0000),
+        //.DIVF(7'b0111111),
+        //.DIVQ(3'b100),
+        // == 96 MHz == (knocks the socks off MCUs)
+        //.DIVR(4'b0000),
+        //.DIVF(7'b0111111),
+        //.DIVQ(3'b011)
+        // == 111 MHz == (fastest I can go while still passing timing tests)
+        //.DIVR(4'b0000),
+        //.DIVF(7'b1001001),
+        //.DIVQ(3'b011)
+        // == 120 MHz == (overclocked -- doesn't pass the icetime analysis, but seems stable)
+        .DIVR(4'b0000),
+        .DIVF(7'b1001111),
+        .DIVQ(3'b011)
+        // == 129 ==
+        // (possibly unreliable -- once saw unstable oscillation, though it's worked since then)
+        //.DIVR(4'b0000),
+        //.DIVF(7'b1010101),
+        //.DIVQ(3'b011)
+        // == 141 == (breakdown -- resulting oscillation is stable but only 35.25MHz)
+        //.DIVR(4'b0000),
+        //.DIVF(7'b0101110),
+        //.DIVQ(3'b010)
+        // == 165 MHz == (breakdown -- resulting oscillation is stable but only 41.25MHz)
+        //.DIVR(4'b0000),
+        //.DIVF(7'b0110110),
+        //.DIVQ(3'b010)
+    ) clk_pll (
+        .PACKAGEPIN(CLK_12MHZ),
+        .PLLOUTGLOBAL(clk),
+        .LOCK(clk_lock),
+        .RESETB(1'b1),
+        .BYPASS(1'b0)
+    );
+
+    always @(posedge clk or negedge clk_lock) begin
+        if (!clk_lock) begin
+            P1A1 <= 1'b0;
+        end else begin
+            P1A1 <= !P1A2;
+        end
+    end
+
+endmodule
diff --git a/index.html b/index.html
index 8725abdc88458793f6c5765e6655c2a237d079c4..dc2fe4b87e5cafd0bf0f35458215a04673f71aa5 100644
--- a/index.html
+++ b/index.html
@@ -39,6 +39,13 @@ The GPIO test measures how quickly pins can communicate with a processor core; t
 <th>date</th>
 </tr>
 
+<tr>
+<td>60.00</td>
+<td>iCE40UP5K</td>
+<td>iCEBreaker V1.0e, 120 MHz, <a href=GPIO/iCE40/ring.v>Verilog</a> (<a href=GPIO/iCE40/README.md>notes</a>)</td>
+<td>September, 2021</td>
+</tr>
+
 <tr>
 <td>23.08</td>
 <td>IMXRT1062</td>