feat(datasheet): add typst datasheet flow

This is a squash of 5 commits in order to add the new datasheet flow.

fix(datasheet): ensure datasheet can use images properly

fix(datasheet): white page when funding was not specified

This was caused by creating a page before first checking whether there was any content for that page beforehand, so it would result in a rogue white page with no content.

fix(datasheet): missing analog pin tables

fix(datasheet): tweak pandoc settings

Switching from `gfm` to `markdown` should enable more formatting options. The extensions have been configured to be similar to `gfm` but provide some additional things, like `line_blocks` and `lists_without_preceding_blankline`s.
This commit is contained in:
Kristaps Jurkans
2025-12-04 14:59:17 +00:00
parent b85e49a27e
commit 7dff87b5ab
37 changed files with 2685 additions and 133 deletions
+52
View File
@@ -0,0 +1,52 @@
#import "../src/lib.typ": annotated_qrcode
#let call_to_action_page(qrcode_colour: black) = [
#align(center)[
#heading(level: 1)[Where is #emph(underline("your")) design?]
*Go from idea to chip design in minutes, without breaking the bank.*
]
Interested and want to get your own design manufactured? Visit our website and
check out our educational material and previous submissions!
#align(center, heading(level: 2, outlined: false)[How?])
New to this? Use our basic Wokwi template to see what's possible. If you're ready for more,
use our advanced Wokwi template and unlock some extra pins.
Know Verilog and CocoTB? Get stuck in with our HDL templates.
#align(center, heading(level: 2, outlined: false)[When?])
Multiple shuttles are run per year, meaning you've got an opportunity to manufacture your design
at any time.
#align(center, heading(level: 2, outlined: false)[Stuck? Need help? Want inspiration?])
Come chat to us and our community on Discord! Scan the QR code below.
#v(1cm)
#grid(
columns: (1fr, 1fr, 1fr),
rows: 1,
column-gutter: 1em,
align: center,
inset: 8pt,
fill: white,
annotated_qrcode(
"https://tinytapeout.com",
"Website",
tiaoma_args: ("scale": 2.0, "fg-color": qrcode_colour)
),
annotated_qrcode(
"https://tinytapeout.com/digital_design",
"Digital design guide",
tiaoma_args: ("scale": 1.75, "fg-color": qrcode_colour)
),
annotated_qrcode(
"https://tinytapeout.com/discord",
"Discord server",
tiaoma_args: ("scale": 2.0, "fg-color": qrcode_colour)
)
)
]
+144
View File
@@ -0,0 +1,144 @@
#let caravel = {
align(center,
table(
columns: 4,
align: left,
table.header(
[mprj_io_pin], [Function], [Signal], [QFN64 pin]
),
`0`, [Input], [`ui_in[0]`], [`31`],
`1`, [Input], [`ui_in[1]`], [`32`],
`2`, [Input], [`ui_in[2]`], [`33`],
`3`, [Input], [`ui_in[3]`], [`34`],
`4`, [Input], [`ui_in[4]`], [`35`],
`5`, [Input], [`ui_in[5]`], [`36`],
`6`, [Input], [`ui_in[6]`], [`37`],
`7`, [Analog], [`analog[0]`], [`41`],
`8`, [Analog], [`analog[1]`], [`42`],
`9`, [Analog], [`analog[2]`], [`43`],
`10`, [Analog], [`analog[3]`], [`44`],
`11`, [Analog], [`analog[4]`], [`45`],
`12`, [Analog], [`analog[5]`], [`46`],
`13`, [Input], [`ui_in[7]`], [48],
`14`, [Input], [`clk` $dagger$], [50],
`15`, [Input], [`rst_n` $dagger$], [`51`],
`16`, [Bidirectional], [`uio[0]`], [`53`],
`17`, [Bidirectional], [`uio[1]`], [`54`],
`18`, [Bidirectional], [`uio[2]`], [`55`],
`19`, [Bidirectional], [`uio[3]`], [`57`],
`20`, [Bidirectional], [`uio[4]`], [`58`],
`21`, [Bidirectional], [`uio[5]`], [`59`],
`22`, [Bidirectional], [`uio[6]`], [`60`],
`23`, [Bidirectional], [`uio[7]`], [`61`],
`24`, [Output], [`uo_out[0]`], [`62`],
`25`, [Output], [`uo_out[1]`], [`2`],
`26`, [Output], [`uo_out[2]`], [`3`],
`27`, [Output], [`uo_out[3]`], [`4`],
`28`, [Output], [`uo_out[4]`], [`5`],
`29`, [Output], [`uo_out[5]`], [`6`],
`30`, [Output], [`uo_out[6]`], [`7`],
`31`, [Output], [`uo_out[7]`], [`8`],
`32`, [Analog], [`analog[6]`], [`11`],
`33`, [Analog], [`analog[7]`], [`12`],
`34`, [Analog], [`analog[8]`], [`13`],
`35`, [Analog], [`analog[9]`], [`14`],
`36`, [Analog], [`analog[10]`], [`15`],
`37`, [Analog], [`analog[11]`], [`16`],
`38`, [Mux Control], [`ctrl_ena`], [`22`],
`39`, [Mux Control], [`ctrl_sel_inc`], [`24`],
`40`, [Mux Control], [`ctrl_sel_rst_n`], [`25`],
`41`, [Reserved], [(none)], [`26`],
`42`, [Reserved], [(none)], [`27`],
`43`, [Reserved], [(none)], [`28`],
)
)
[
$dagger$ Internally, there's no difference between `clk`, `rst_n` and `ui_in` pins. They are all just bits in the
`pad_ui_in` bus. However, we use different names to make it easier to understand the purpose of each signal.
]
}
#let openframe = {
align(center,
table(
columns: 3,
align: left,
table.header(
[QFN64 Pin], [Function], [Signal]
),
`1`, [Mux Control], [`ctrl_ena`],
`2`, [Mux Control], [`ctrl_sel_inc`],
`3`, [Mux Control], [`ctrl_sel_rst_n`],
`4`, [Reserved], [(none)],
`5`, [Reserved], [(none)],
`6`, [Reserved], [(none)],
`7`, [Reserved], [(none)],
`8`, [Reserved], [(none)],
`9`, [Output], [`uo_out[0]`],
`10`, [Output], [`uo_out[1]`],
`11`, [Output], [`uo_out[2]`],
`12`, [Output], [`uo_out[3]`],
`13`, [Output], [`uo_out[4]`],
`14`, [Output], [`uo_out[5]`],
`15`, [Output], [`uo_out[6]`],
`16`, [Output], [`uo_out[7]`],
`17`, [Power], [VDD IO],
`18`, [Ground], [GND IO],
`19`, [Analog], [`analog[0]`],
`20`, [Analog], [`analog[1]`],
`21`, [Analog], [`analog[2]`],
`22`, [Analog], [`analog[3]`],
`23`, [Power], [VAA Analog],
`24`, [Ground], [GND Analog],
`25`, [Analog], [`analog[4]`],
`26`, [Analog], [`analog[5]`],
`27`, [Analog], [`analog[6]`],
`28`, [Analog], [`analog[7]`],
`29`, [Ground], [VDD Core],
`30`, [Power], [VDD Core],
`31`, [Ground], [GND IO],
`32`, [Power], [VDD IO],
`33`, [Bidirectional], [`uio[0]`],
`34`, [Bidirectional], [`uio[1]`],
`35`, [Bidirectional], [`uio[2]`],
`36`, [Bidirectional], [`uio[3]`],
`37`, [Bidirectional], [`uio[4]`],
`38`, [Bidirectional], [`uio[5]`],
`39`, [Bidirectional], [`uio[6]`],
`40`, [Bidirectional], [`uio[7]`],
`41`, [Input], [`ui_in[0]`],
`42`, [Input], [`ui_in[1]`],
`43`, [Input], [`ui_in[2]`],
`44`, [Input], [`ui_in[3]`],
`45`, [Input], [`ui_in[4]`],
`46`, [Input], [`ui_in[5]`],
`47`, [Input], [`ui_in[6]`],
`48`, [Input], [`ui_in[7]`],
`49`, [Input], [`rst_n` $dagger$],
`50`, [Input], [`clk` $dagger$],
`51`, [Ground], [GND IO],
`52`, [Power], [VDD IO],
`53`, [Analog], [`analog[8]`],
`54`, [Analog], [`analog[9]`],
`55`, [Analog], [`analog[10]`],
`56`, [Analog], [`analog[11]`],
`57`, [Ground], [GND Analog],
`58`, [Power], [VDD Analog],
`59`, [Analog], [`analog[12]`],
`60`, [Analog], [`analog[13]`],
`61`, [Analog], [`analog[14]`],
`62`, [Analog], [`analog[15]`],
`63`, [Ground], [GND Core],
`64`, [Power], [VDD Core]
)
)
[
$dagger$ Internally, there's no difference between `clk`, `rst_n` and `ui_in` pins. They are all just bits in the
`pad_ui_in` bus. However, we use different names to make it easier to understand the purpose of each signal.
]
}
+11
View File
@@ -0,0 +1,11 @@
#import "../src/lib.typ": get_image_by_id
= Pinout
The chip is packaged in a 64-pin QFN package. The pinout is shown below.
#align(center, get_image_by_id("images", "chip_pinout_qfn64"))
Note: You will receive the chip mounted on a breakout board
(#link("https://github.com/tinytapeout/breakout-pcb")[github.com/tinytapeout/breakout-pcb]).
The pinout is provided for advanced users, as most users will not need to solder the chip directly.
+21
View File
@@ -0,0 +1,21 @@
= Team
Tiny Tapeout would not be possible without a lot of people helping. We would especially like to thank:
- *Uri Shaked* for #link("https://wokwi.com")[Wokwi] development and lots more
- #link("https://psychogenic.com/")[*Patrick Deegan*] for PCBs, software, documentation and lots more
- #link("https://twitter.com/tnt")[*Sylvain Munaut*] for help with scan chain improvements
- #link("https://www.linkedin.com/in/michael-thompson-0a581a/")[*Mike Thompson*] and
#link("https://www.linkedin.com/in/mitch-bailey-8ba0b45/")[*Mitch Bailey*] for verification expertise
- #link("https://www.linkedin.com/in/tim-edwards-4376a18/")[*Tim Edwards*] and
#link("https://www.linkedin.com/in/harald-pretl-4911ba10/")[*Harald Pretl*] for ASIC expertise
- #link("https://twitter.com/jix_")[*Jix*] for formal verification support
- #link("https://twitter.com/proppy")[*Proppy*] for help with GitHub actions
- #link("https://twitter.com/maxiborga")[*Maximo Balestrini*] for all the amazing renders and the interactive GDS viewer
- *James Rosenthal* for coming up with digital design examples
- All the *people who took part in TinyTapeout 01* and volunteered time to improve docs and test the flow
- The #link("https://www.yosyshq.com/")[*team at YosysHQ*] and *all the other open source EDA tool makes*
- *Jeff* and the #link("https://efabless.com/")[*Efabless Team*] for running the shuttles and providing OpenLane and
sponsorship
- #link("https://www.youtube.com/watch?v=EczW2IWdnOM")[*Tim Ansell* and *Google*] for supporting the open source silicon
movement
- #link("https://zerotoasiccourse.com/")[*Zero to ASIC course community*] for all your support
- *Jeremy Birch* for help with STA
+97
View File
@@ -0,0 +1,97 @@
#import "../src/lib.typ": get_image_by_id
= The Tiny Tapeout Multiplexer
== Overview
The Tiny Tapeout Multiplexer distributes a single set of user IOs to multiple user designs. It is the backbone of the
Tiny Tapeout chip.
It has the following features:
- 10 dedicated inputs
- 8 dedicated outputs
- 8 bidirectional outputs
- Supports up to 512 user designs (32 mux units, each with up to 16 designs)
- Designs can have different sizes. The basic unit is called a tile, and each design can occupy up to 16 tiles.
== Operation
The multiplexer consists of three main units:
1. The controller $dash.em$ used to set the address of the active design
2. The spine $dash.em$ a bus that connects the controller with all the mux units
3. Mux units $dash.em$ connects the spine to individual user designs
=== The Controller
The mux controller has 3 input lines:
#align(center)[
#table(
columns: 2, rows: 4,
table.header(
[Input], [Description]
),
`ena`, [Sent as-is (buffered) to the downstream mux units],
`sel_rst_n`, [Resets the internal address counter to 0 (active low)],
`sel_inc`, [Increments the internal address counter by 1]
)
]
It outputs the address of the currently selected design on the `si_sel` port of the spine (see below).
For instance, to select the design at address 12, you need to pulse `sel_rst_n` low, and then pulse `sel_inc` 12 times:
#figure(
caption: [Mux signals for activating the design at address 12],
align(center, get_image_by_id("images", "mux_address_select_waveform"))
)
#figure(
caption: [Mux Diagram],
align(center, get_image_by_id("images", "mux_diagram", height: 55%))
)
#figure(
caption: [Mux Controller Diagram],
align(center, get_image_by_id("images", "mux_controller"))
)
Internally, the controller is just a chain of 10 D-flip-flops. The `sel_inc` signal is connected to the clock of the
first flip-flop, and the output of each flip-flop is connected to the clock of the next flip-flop. The `sel_rst_n`
signal is connected to the reset of all flip-flops.
The following Wokwi project demonstrates this setup:
#link("https://wokwi.com/projects/36434780766")[wokwi.com/projects/36434780766].
It contains an Arduino Nano that decodes the currently selected mux address and displays it on a 7-segment display.
Click on the button labeled `RST_N` to reset the counter, and click on the button labeled `INC` to increment the counter.
=== The Spine
The controller and all muxes are connected together through the spine. The spine has the following signals going to it:
*From controller to mux*:
- `si_ena` $dash.em$ the `ena` input
- `si_sel` $dash.em$ selected design address (10 bits)
- `ui_in` $dash.em$ user clock, user `rst_n`, user_inputs (10 bits)
- `uio_in` $dash.em$ bidirectional I/O inputs (8 bits)
*From mux to controller*:
- `uo_out` $dash.em$ user outputs (8 bits)
- `uio_oe` $dash.em$ bidirectional I/O output enable (8 bits)
- `uio_out` $dash.em$ bidirectional I/O outputs (8 bits)
The only signal which is actually generated by the controller is `si_sel` (using `sel_rst_n` and `sel_inc`, as explained
above). The other signals are just going through from/to chip I/O pads.
=== The Multiplexer
Each mux branch is connected to up to 16 designs. It also has 5 bits of a hard-coded address (each unit gets assigned
a different address, based on its position on the die).
The mux implements the following logic:
If `si_ena` is 1, and `si_sel` matches the mux address, we know the mux is active. Then, it activates the specific
user design port that matches the remaining bits of `si_sel`.
*For the active design*:
- `clk`, `rst_n`, `ui_in`, `uio_in` are connected to the respective pins coming from the spine (through a buffer)
- `uo_out`, `uio_oe`, `uio_out` are connected to the respective pins going out to the spine (through a tristate buffer)
*For all others, inactive designs* (including all designs in inactive muxes):
- `clk`, `rst_n`, `ui_in`, `uio_in` are all tied to zero
- `uo_out`, `uio_oe`, `uio_out` are disconnected from the spine (the tristate buffer output enable is disabled)
== Pinout
// this pinout table must be populated from within the datasheet template itself
@@ -0,0 +1,92 @@
#import "../src/lib.typ": badge, callout
#import "../src/colours.typ"
= Using This Datasheet
== Structure
Projects are ordered by their mux address, in ascending order. Documentation is user-provided from their GitHub
repositories and are merged into the final shuttle once the deadline is reached.
In general, each project should contain:
- The user-provided title & a list of authors
- A link to the GitHub repository used for submission
- A link to the Wokwi project (if applicable)
- A "How it works" section
- A "How to test" section
- An "External hardware" section (if applicable)
- A pinout table for both digital & analog designs
== Badges
This datasheet uses "badges" to quickly convey some information about the content. These badges are explained in the
table below.
#let example_address_badge = box(baseline: 0.4em, badge(colours.BADGE_TEAL, strong(raw("-123"))))
#let example_microtile_addr_badge = box(baseline: 0.4em, badge(colours.BADGE_TEAL, strong(raw("-423/2"))))
#let example_clock_badge = box(baseline: 0.4em, badge(colours.BADGE_BLUE, strong(raw("25.175 MHz"))))
#let example_proj_wokwi = box(baseline: 0.4em, badge(colours.BADGE_PINK, strong(raw("Wokwi Project"))))
#let example_proj_hdl = box(baseline: 0.4em, badge(colours.BADGE_PINK, strong(raw("HDL Project"))))
#let example_proj_analog = box(baseline: 0.4em, badge(colours.BADGE_PINK, strong(raw("Analog Project"))))
#let example_proj_analog = box(baseline: 0.4em, badge(colours.BADGE_PINK, strong(raw("Analog Project"))))
#let example_medium_danger = box(baseline: 0.4em, badge(colours.WARNING_STRONG, strong(raw("Medium Danger"))))
#let example_high_danger = box(baseline: 0.4em, badge(colours.DANGER_STRONG, strong(raw("High Danger"))))
#let example_artwork = box(baseline: 0.4em, badge(colours.BADGE_PURPLE, strong(raw("Artwork"))))
#table(
columns: 2,
inset: 6pt,
align: (center + horizon, left),
table.header(
[Badge], [Description]
),
example_artwork, [Used to showcase artwork from our community.],
[#example_address_badge \ #example_microtile_addr_badge], [Mux address of the project, in decimal. For microtile
designs, their sub-address is placed after the forward slash. In this example, it would be `2`.],
example_clock_badge, [Clock frequency of the project. May be truncated from actual value or omitted completely.],
[#example_proj_hdl \ #example_proj_wokwi \ #example_proj_analog], [Project type, indicating if it was made with a HDL,
Wokwi, or if it is analog.],
[#example_medium_danger \ #example_high_danger], [Indicates the risk that the project presents to the ASIC. Medium
danger projects can damage the ASIC under certain conditions, whilst high danger projects _will_ damage the ASIC.]
)
== Callouts
In addition to #example_medium_danger and #example_high_danger badges being used, a callout is placed before the project
documentation begins to alert the user.
A callout for #example_medium_danger may look something like:
#callout("warning",
`This project will damage the ASIC under certain conditions.`,
`There is an error in the schematic which may lead to ASIC failure under certain clocking conditions.`
)
Similarly, a callout for #example_high_danger may look something like:
#callout("danger",
`This project will damage the ASIC.`,
`There is an error in the schematic which may cause permanent damage when powered on in a certain configuration.`
)
Should there be a project that poses a danger, the callout will explain the reasoning behind the danger level.
Callouts may also provide some additional information, and look something like so:
#callout("info",
`Information`,
raw("Silicon melts at 1414°C, and boils at 3265°C. Don't let your chip get too hot!")
)
== Figures & Footnotes
Numbering for figures and footnotes within the "Project" chapter is formed by combining the address of the project with
the current figure number. For example, the second figure for a project with an address of 256 will be captioned with
"Figure 256.2". Likewise, the third footnote for a project of address 128 will be shown as "128.3".
The numbering outside of the "Project" chapter resumes as normal, being formatted with a simple number, e.g. "Figure 3".
== Updates
This datasheet is intended to be a living and breathing document. Please update your projects' datasheet with new
information if you have it, by creating a pull request against the shuttle repository.