2025-08-30
On the encouragement of my friend Nanik Adnani, I participated in Tiny Tapeout 7 not long ago. While I had written Verilog and VHDL (both languages for hardware description) for the purpose of making designs for an FPGA (often the DE10 from terasIC), I had never really dreamed of being able to design an ASIC.
I participated in “shuttle” 7, which used the now defunct efabless to actually create the chips.

The board as shown here is actually one board stacked on another. The bottom one is the “carrier board”, which has connectors, switches, buttons, and a glorious 7-segment display. Atop it lives the QFN (quad flat no-leads package), which is the little black square on top of the “daughterboard”.
The actual chip lives inside the QFN (which is mostly a plastic shell and little leads).
Moritz94 of Wikipedia provides this image (with attached license) of the QFN internally.
Inside the QFN is the chip, which is rendered here as such…

This shows the different layers of the chip inside, which is mostly made from elaborating the logic explained within the Verilog HDL I wrote into NAND gates, which are arranged out of transistors.
Here’s a diagram that I made that shows how you can design a NAND gate using transistors (both PMOS and NMOS). The PMOS create a pull up network while the NMOS create a pull down network. A PMOS is noted via a circle at the gate terminal.
Vss is low voltage, whereas Vdd is high voltage. If either A or B is low, then Out is mapped to Vdd, if A and B are both high though, Out is mapped to Vss. This maps to a NAND gate, where only (nand #t #t) is #f, everything else being #t.
Thanks to kdp1965 from the Tiny Tapeout Discord for resurrecting my original Verilog code. I cannot quite recall how I originally deleted my submission, but I imagine that it had something to do with my propensity to try to migrate off of Github and fondness for scripts that carelessly invoke
gh.
Reproduced below is my project.
/*
* Copyright (c) 2024 Aiden Fox Ivey
* SPDX-License-Identifier: Apache-2.0
*/
`default_nettype none
module tt_um_aidenfoxivey (
input wire [7:0] ui_in, // Dedicated inputs
output wire [7:0] uo_out, // Dedicated outputs
input wire [7:0] uio_in, // IOs: Input path
output wire [7:0] uio_out, // IOs: Output path
output wire [7:0] uio_oe, // IOs: Enable path (active high: 0=input, 1=output)
input wire ena, // always 1 when the design is powered, so you can ignore it
input wire clk, // clock
input wire rst_n // reset_n - low to reset
);
assign uio_out = 0;
assign uio_oe = 0;
crc8 crc (
.data_in(ui_in),
.data_out(uo_out),
.rst(!rst_n),
.clk(clk),
.enable(uio_in[0])
);
endmodule
// 1+x^1+x^2+x^8;
module crc8 (
input wire [7:0] data_in,
output wire [7:0] data_out,
input wire rst,
input wire clk,
input wire enable
);
reg [7:0] next_crc, curr_crc;
assign data_out = curr_crc;
always @(posedge clk or posedge rst) begin
if (rst) begin
curr_crc <= {8{1'b0}}; // CCITT initial value is defined as 0
end
else begin
curr_crc <= enable ? next_crc : curr_crc;
end
end // always
always @(*) begin
next_crc[0] = curr_crc[0] ^ curr_crc[6] ^ curr_crc[7] ^ data_in[0] ^ data_in[6] ^ data_in[7];
next_crc[1] = curr_crc[0] ^ curr_crc[1] ^ curr_crc[6] ^ data_in[0] ^ data_in[1] ^ data_in[6];
next_crc[2] = curr_crc[0] ^ curr_crc[1] ^ curr_crc[2] ^ curr_crc[6] ^ data_in[0] ^ data_in[1] ^ data_in[2] ^ data_in[6];
next_crc[3] = curr_crc[1] ^ curr_crc[2] ^ curr_crc[3] ^ curr_crc[7] ^ data_in[1] ^ data_in[2] ^ data_in[3] ^ data_in[7];
next_crc[4] = curr_crc[2] ^ curr_crc[3] ^ curr_crc[4] ^ data_in[2] ^ data_in[3] ^ data_in[4];
next_crc[5] = curr_crc[3] ^ curr_crc[4] ^ curr_crc[5] ^ data_in[3] ^ data_in[4] ^ data_in[5];
next_crc[6] = curr_crc[4] ^ curr_crc[5] ^ curr_crc[6] ^ data_in[4] ^ data_in[5] ^ data_in[6];
next_crc[7] = curr_crc[5] ^ curr_crc[6] ^ curr_crc[7] ^ data_in[5] ^ data_in[6] ^ data_in[7];
end // always
endmodule // crc8
As you can see, it is extremely simple. I was mostly eager to get a design taped out and also left this project for one weekend. (Don’t be like me - plan out your projects and work in small chunks if you have the option / ability.)
I think I also wrote a test for my submission, but that cocotb code is now missing in action.
The actual gist of my project is a very simple hardware implementation of a cyclic redundancy check that produces 8-bit values following the CCITT standard. CCITT here being the Comité Consultatif International Téléphonique et Télégraphique, which is the old name of the ITU-T.
Essentially, this CRC-8 implementation is used for telecommunications! I picked it because… actually I’ll be honest, I have no idea why I picked it. I never wrote that part down.
A brief explanation of the cyclic redundancy check is that it is a method that can be used to correct errors when data is sent over a medium.
Before we proceed, say this with me: CRCs are not substitutes for digital signatures or message authentication codes.
Basically, don’t use a CRC and expect your scheme to be resistant towards intentional modification. CRCs do not provide authentication.
Essentially, the approach for calculating the CRC of some specific value requires dividing the padded binary value by a divisor that is specified for each specific standard of CRC. The remainder of this division is then divided itself repeatedly, until the dividend is equal to 0. The remainder value is returned.
If the value isn’t modified in transit, then dividing the returned value by the remainder value from the first part should yield all zeroes for a dividend and all zeroes for a remainder. Otherwise, there was a bit error.
The Wikipedia page for CRCs has a good summary on why this works, as well as a more precise description of how it works than what I offered.
For CRC-8 CCITT, using a handy calculator will tell you that the value of 0xAF corresponds to a value of 0x44.
It works!! You can read more about the specific project here.
In the future I’d really like to try implementing some subset of the RISC-V standard.
That’s all for now though. :)