diff --git a/.github/workflows/mdbook.yml b/.github/workflows/mdbook.yml new file mode 100644 index 0000000..1291608 --- /dev/null +++ b/.github/workflows/mdbook.yml @@ -0,0 +1,75 @@ +# Sample workflow for building and deploying a mdBook site to GitHub Pages +# +# To get started with mdBook see: https://rust-lang.github.io/mdBook/index.html +# +name: Deploy mdBook site to Pages + +on: + # Runs on pushes targeting the default branch + push: + branches: ["main"] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + # Build job + build: + runs-on: ubuntu-latest + env: + MDBOOK_VERSION: 0.4.36 + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Cache cargo registry and mdBook binary + id: cache-mdbook + uses: actions/cache@v4 + with: + path: | + ~/.cargo/bin/mdbook + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + key: ${{ runner.os }}-cargo-mdbook-${{ env.MDBOOK_VERSION }} + + - name: Install mdBook + if: steps.cache-mdbook.outputs.cache-hit != 'true' + run: | + curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf -y | sh + rustup update + cargo install --version ${MDBOOK_VERSION} mdbook + - name: Setup Pages + id: pages + uses: actions/configure-pages@v5 + - name: Build with mdBook + run: mdbook build + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: ./book + + # Deployment job + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v5 diff --git a/.gitignore b/.gitignore index 139d40b..7585238 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1 @@ -notes.txt -index.html +book diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..9279b3c --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "theme"] + path = theme + url = git@github.com:beastr45/ASIC-network_MDBookTheme.git diff --git a/Pages/TEMPLATE.md b/Pages/TEMPLATE.md deleted file mode 100644 index b4a02c8..0000000 --- a/Pages/TEMPLATE.md +++ /dev/null @@ -1,50 +0,0 @@ - - -# Project Title - -One or two sentences on what this page covers and what the reader will have working at the end. - -## Overview - -Context, theory, background. - -## Prerequisites - -- tool or PDK -- prior page: [example](page_1.md) - -## Steps - -1. first step -2. second step - -```bash -# commands go in fenced blocks with a language tag -echo "hello" -``` - -## Results - -| metric | value | -|--------|------:| -| example | 1.0 | - -## Checklist - -- [ ] item - ---- - -*Questions? Ask in the network Discord.* diff --git a/Pages/img/images.md b/Pages/img/images.md deleted file mode 100644 index c5bd2e0..0000000 --- a/Pages/img/images.md +++ /dev/null @@ -1 +0,0 @@ -images go here :) diff --git a/README.md b/README.md index 7ff56db..42e5093 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,31 @@ +# analog-tutorials + +Open documentation for analog IC design, built with [mdBook](https://rust-lang.github.io/mdBook/). + **This is still a work in progress.** +## Clone + +`theme/` is a git submodule (shared with the rest of the network's docs +books — see [asic-network-docs/theme](../theme)): + +``` +git clone --recurse-submodules +# or, after a plain clone: +git submodule update --init +``` + +## Preview locally + +``` +mdbook serve +``` + +## Writing pages + +One `src/*.md` file per page, listed in order in `src/SUMMARY.md`. Copy +`src/TEMPLATE.md` to start a new one — see the comment at the top of that +file for the authoring rules (linking, images, etc). + # To-do * add an authors list section at the end of the pages so its easy for newer people to reach out to the right person if they are willing to contribute diff --git a/TEMPLATE.md b/TEMPLATE.md deleted file mode 100644 index dad82b2..0000000 --- a/TEMPLATE.md +++ /dev/null @@ -1,50 +0,0 @@ - - -# Project Title - -One or two sentences on what this page covers and what the reader will have working at the end. - -## Overview - -Context, theory, background. - -## Prerequisites - -- tool or PDK -- prior page: [example](page_1.md) - -## Steps - -1. first step -2. second step - -```bash -# commands go in fenced blocks with a language tag -echo "hello" -``` - -## Results - -| metric | value | -|--------|------:| -| example | 1.0 | - -## Checklist - -- [ ] item - ---- - -*Questions? Ask in the network Discord.* diff --git a/app.js b/app.js deleted file mode 100644 index af4e6ad..0000000 --- a/app.js +++ /dev/null @@ -1,341 +0,0 @@ -/* ========================================================= - CONFIG: edit these two strings per repo, nothing else - ========================================================= */ -const CONFIG = { - title: "Analog Tutorials", // EDIT: "Analog Tutorials" / "Digital Tutorials" - repo: "https://github.com/ASIC-Network/analog-tutorials", // EDIT: this repo's github url - mainSite: "https://asicnetwork.net", - branch: "main", - pagesDir: "Pages", - maxPages: 60 -}; - -/* ---------- dom refs + boilerplate ---------- */ -const $id = s => document.getElementById(s); -const content = $id("content"), pageNav = $id("page-nav"), pager = $id("pager"), - metaEl = $id("doc-meta"), tocNav = $id("toc-nav"), tocBox = $id("toc"), - tbTitle = $id("tb-title"); - -document.title = CONFIG.title + " \u00b7 ASIC Network"; -$id("brand-sub").textContent = CONFIG.title; -$id("nav-home").href = CONFIG.mainSite; -$id("nav-main").href = CONFIG.mainSite; -$id("nav-repo").href = CONFIG.repo; -$id("f-repo").href = CONFIG.repo; - -/* scroll progress */ -(function(){ - const bar = $id("progress"); - function upd(){ - const h = document.documentElement; - const max = h.scrollHeight - h.clientHeight; - bar.style.transform = "scaleX(" + (max > 0 ? h.scrollTop / max : 0) + ")"; - } - addEventListener("scroll", upd, { passive: true }); - addEventListener("resize", upd); - upd(); -})(); - -/* drawer */ -(function(){ - const body = document.body; - $id("drawer-btn").addEventListener("click", () => body.classList.toggle("drawer-open")); - $id("backdrop").addEventListener("click", () => body.classList.remove("drawer-open")); - pageNav.addEventListener("click", e => { if (e.target.closest("a")) body.classList.remove("drawer-open"); }); -})(); - -/* ========================================================= - markdown renderer - headers, emphasis, code, lists, tasks, tables, quotes, - images, links, hr. raw HTML is escaped, comments stripped. - ========================================================= */ -/*__MD_START__*/ -var MD_BASE = ""; -function setMdBase(b){ MD_BASE = b || ""; } -function absUrl(u){ return /^(?:[a-z][a-z0-9+.-]*:|\/|#)/i.test(u) ? u : MD_BASE + u; } -var slugCounts = Object.create(null); -function resetSlugs(){ slugCounts = Object.create(null); } -var ESC_MAP = {"&":"&","<":"<",">":">",'"':""","'":"'"}; -function escHtml(s){ return String(s).replace(/[&<>"']/g, function(m){ return ESC_MAP[m]; }); } -function slug(text){ - var s = text.toLowerCase().replace(/`/g,"").replace(/[^a-z0-9\u00c0-\u024f]+/g,"-").replace(/^-+|-+$/g,"") || "section"; - var n = slugCounts[s] || 0; slugCounts[s] = n + 1; - return n ? s + "-" + n : s; -} -function emphasize(s){ - s = s.replace(/\*\*\*([^*]+)\*\*\*/g,"$1"); - s = s.replace(/\*\*([^*]+)\*\*/g,"$1"); - s = s.replace(/(^|[^\w*])\*([^*\n]+)\*(?![\w*])/g,"$1$2"); - s = s.replace(/(^|[^\w_])_([^_\n]+)_(?![\w_])/g,"$1$2"); - s = s.replace(/~~([^~]+)~~/g,"$1"); - return s; -} -function inline(raw){ - var stash = []; - function keep(h){ stash.push(h); return "\u0001" + (stash.length - 1) + "\u0001"; } - var s = escHtml(raw); - s = s.replace(/`([^`]+)`/g, function(m,c){ return keep("" + c + ""); }); - s = s.replace(/!\[([^\]]*)\]\(([^)\s]+)(?:\s+"[^&]*")?\)/g, function(m,alt,src){ - return keep('' + alt + ''); - }); - s = s.replace(/\[([^\]]+)\]\(([^)\s]+)(?:\s+"[^&]*")?\)/g, function(m,t,u){ - var pm = u.match(/^page[_-]?(\d+)\.md$/i); - var href = pm ? "#page_" + pm[1] : absUrl(u); - var ext = /^https?:/i.test(href); - return keep('" + emphasize(t) + ""); - }); - s = emphasize(s); - s = s.replace(/\u0001(\d+)\u0001/g, function(m,i){ return stash[+i]; }); - return s; -} -function splitRow(row){ - var r = row.trim(); - if (r.charAt(0) === "|") r = r.slice(1); - if (r.charAt(r.length - 1) === "|") r = r.slice(0, -1); - return r.split("|").map(function(c){ return c.trim(); }); -} -function buildList(items){ - var html = "", stack = [], prev = -1; - items.forEach(function(it){ - var lvl = Math.min(5, Math.floor(it.ind / 2)); - if (lvl > prev){ - for (var k = prev; k < lvl; k++){ html += it.ord ? "
    " : "
" : "") + ""; } - if (stack.length && stack[stack.length - 1] !== it.ord){ - html += stack.pop() ? "" : ""; - html += it.ord ? "
    " : "
" : ""; if (stack.length) html += ""; } - return html; -} -function mdToHtml(src){ - src = String(src).replace(/^\uFEFF/,"").replace(/\r\n?/g,"\n").replace(/\t/g," ").replace(//g,""); - var lines = src.split("\n"), out = [], para = [], i = 0; - function flush(){ if (para.length){ out.push("

" + inline(para.join(" ")) + "

"); para = []; } } - while (i < lines.length){ - var line = lines[i]; - - var fence = line.match(/^\s*```(.*)$/); - if (fence){ - flush(); - var lang = fence[1].trim(), buf = []; i++; - while (i < lines.length && !/^\s*```\s*$/.test(lines[i])){ buf.push(lines[i]); i++; } - i++; - out.push('
' + escHtml(lang || "code") - + '
' + escHtml(buf.join("\n")) + "
"); - continue; - } - - if (/^\s*$/.test(line)){ flush(); i++; continue; } - - var h = line.match(/^(#{1,6})\s+(.*)$/); - if (h){ - flush(); - var lvl = h[1].length, txt = h[2].replace(/\s+#+\s*$/,"").trim(), hid = slug(txt); - out.push("' + inline(txt) + '"); - i++; continue; - } - - /* table before hr so |---|---| separators are not eaten */ - if (line.indexOf("|") !== -1 && i + 1 < lines.length && lines[i+1].indexOf("|") !== -1 - && /^\s*\|?[\s:|-]+\|?\s*$/.test(lines[i+1]) && lines[i+1].indexOf("-") !== -1){ - flush(); - var head = splitRow(line); - var aligns = splitRow(lines[i+1]).map(function(c){ - var al = /^:/.test(c), ar = /:$/.test(c); - return al && ar ? "center" : ar ? "right" : ""; - }); - i += 2; - var rows = []; - while (i < lines.length && lines[i].indexOf("|") !== -1 && !/^\s*$/.test(lines[i])){ rows.push(splitRow(lines[i])); i++; } - var alAttr = function(k){ return aligns[k] ? ' style="text-align:' + aligns[k] + '"' : ""; }; - out.push('
' - + head.map(function(c,k){ return "" + inline(c) + ""; }).join("") - + "" - + rows.map(function(r){ return "" + head.map(function(_,k){ return "" + inline(r[k] || "") + ""; }).join("") + ""; }).join("") - + "
"); - continue; - } - - if (/^\s*(?:-{3,}|\*{3,}|_{3,})\s*$/.test(line)){ flush(); out.push("
"); i++; continue; } - - if (/^\s*>/.test(line)){ - flush(); - var qbuf = []; - while (i < lines.length && /^\s*>/.test(lines[i])){ qbuf.push(lines[i].replace(/^\s*>\s?/,"")); i++; } - out.push("
" + mdToHtml(qbuf.join("\n")) + "
"); - continue; - } - - var isLi = line.match(/^(\s*)([-*+]|\d+[.)])\s+(.*)$/); - if (isLi){ - flush(); - var items = []; - while (i < lines.length){ - var m = lines[i].match(/^(\s*)([-*+]|\d+[.)])\s+(.*)$/); - if (m){ items.push({ ind: m[1].length, ord: /^\d/.test(m[2]), txt: [m[3]] }); i++; } - else if (/^\s*$/.test(lines[i])){ - if (i + 1 < lines.length && /^(\s*)([-*+]|\d+[.)])\s+/.test(lines[i+1])) i++; - else break; - } - else if (/^\s{2,}\S/.test(lines[i]) && items.length){ items[items.length - 1].txt.push(lines[i].trim()); i++; } - else break; - } - out.push(buildList(items)); - continue; - } - - var img = line.match(/^\s*!\[([^\]]*)\]\((\S+?)(?:\s+"([^"]*)")?\)\s*$/); - if (img){ - flush(); - var cap = img[3] || img[1]; - out.push('
' + escHtml(img[1]) + '' - + (cap ? "
" + escHtml(cap) + "
" : "") + "
"); - i++; continue; - } - - para.push(line.trim()); i++; - } - flush(); - return out.join("\n"); -} -/*__MD_END__*/ - -/* ---------- page discovery ---------- */ -const PDIR = CONFIG.pagesDir ? CONFIG.pagesDir.replace(/\/+$/, "") + "/" : ""; -setMdBase(PDIR); -function titleOf(src, n){ - var m = String(src).replace(/\r/g,"").match(/^#\s+(.+)$/m); - return m ? m[1].replace(/\s+#+\s*$/,"").trim() : "Page " + n; -} -async function discoverPages(){ - const pages = []; - const B = 6; - for (let s = 1; s <= CONFIG.maxPages; s += B){ - const ns = []; - for (let k = 0; k < B && s + k <= CONFIG.maxPages; k++) ns.push(s + k); - const got = await Promise.all(ns.map(n => - fetch(PDIR + "page_" + n + ".md", { cache: "no-store" }) - .then(r => r.ok ? r.text() : null) - .catch(() => null))); - let gap = false; - got.forEach((txt, k) => { - if (gap) return; - if (txt == null){ gap = true; return; } - pages.push({ n: ns[k], src: txt, title: titleOf(txt, ns[k]) }); - }); - if (gap) break; - } - return pages; -} - -/* ---------- rendering ---------- */ -let PAGES = []; -const pad = n => String(n).padStart(2, "0"); - -function renderNav(active){ - pageNav.innerHTML = PAGES.map((p, k) => - '' - + '' + pad(p.n) + '' + escHtml(p.title) + '').join(""); -} -function renderPager(k){ - const prev = PAGES[k - 1], next = PAGES[k + 1]; - pager.innerHTML = - (prev ? '" : "") - + (next ? '" : ""); -} -let spy = null; -function buildToc(){ - const hs = Array.from(content.querySelectorAll("h2, h3")); - tocBox.style.display = hs.length ? "" : "none"; - tocNav.innerHTML = hs.map(h => { - const c = h.cloneNode(true); - const a = c.querySelector(".hlink"); if (a) a.remove(); - return '' + escHtml(c.textContent.trim()) + ""; - }).join(""); - if (spy) spy.disconnect(); - if (!("IntersectionObserver" in window) || !hs.length) return; - const links = tocNav.querySelectorAll("a"); - spy = new IntersectionObserver(es => { - es.forEach(e => { - if (e.isIntersecting){ - links.forEach(l => l.classList.toggle("active", l.getAttribute("href") === "#" + e.target.id)); - } - }); - }, { rootMargin: "-80px 0px -70% 0px" }); - hs.forEach(h => spy.observe(h)); -} -function bindCopy(){ - content.querySelectorAll(".codeblock .copy").forEach(btn => { - btn.addEventListener("click", () => { - const code = btn.closest(".codeblock").querySelector("code").textContent; - if (navigator.clipboard && navigator.clipboard.writeText){ - navigator.clipboard.writeText(code).then(() => { - btn.textContent = "copied"; - setTimeout(() => { btn.textContent = "copy"; }, 1400); - }); - } - }); - }); -} -function renderPage(k){ - const p = PAGES[k]; - resetSlugs(); - content.innerHTML = mdToHtml(p.src); - document.title = p.title + " \u00b7 " + CONFIG.title; - tbTitle.textContent = p.title; - metaEl.style.display = ""; - metaEl.innerHTML = 'Page ' + pad(p.n) + " / " + pad(PAGES[PAGES.length - 1].n) + "" - + 'edit this page on github ↗'; - renderNav(k); renderPager(k); buildToc(); bindCopy(); - try { window.scrollTo(0, 0); } catch (e) {} -} -function renderEmpty(){ - const isFile = location.protocol === "file:"; - pageNav.innerHTML = ""; - metaEl.style.display = "none"; - tocBox.style.display = "none"; - tbTitle.textContent = "No pages"; - content.innerHTML = '

No pages found

' - + "

This site renders Pages/page_1.md, Pages/page_2.md, … Add Pages/page_1.md (copy Pages/TEMPLATE.md) and reload. Numbering must be sequential with no gaps.

" - + (isFile ? "

You opened this file directly. fetch() needs a server: run python3 -m http.server in this folder and open http://localhost:8000.

" : "") - + "
"; -} - -/* ---------- routing ---------- */ -function idxFromHash(){ - const m = location.hash.match(/^#page[_-](\d+)$/i); - if (!m) return 0; - const k = PAGES.findIndex(p => p.n === +m[1]); - return k >= 0 ? k : 0; -} -addEventListener("hashchange", () => { - const m = location.hash.match(/^#page[_-](\d+)$/i); - if (m && PAGES.length){ - const k = PAGES.findIndex(p => p.n === +m[1]); - if (k >= 0) renderPage(k); - } - /* any other hash is an in-page anchor; the browser handles it */ -}); - -(async function init(){ - PAGES = await discoverPages(); - if (!PAGES.length){ renderEmpty(); return; } - renderPage(idxFromHash()); - if (location.hash && !/^#page[_-]\d+$/i.test(location.hash)){ - const el = document.getElementById(decodeURIComponent(location.hash.slice(1))); - if (el && el.scrollIntoView) el.scrollIntoView(); - } -})(); diff --git a/book.toml b/book.toml new file mode 100644 index 0000000..78461c9 --- /dev/null +++ b/book.toml @@ -0,0 +1,28 @@ +[book] +title = "Analog Tutorials" +authors = ["ASIC Network"] +language = "en" +src = "src" + +[build] +create-missing = false + +[output.html] +git-repository-url = "https://github.com/ASIC-Network/analog-tutorials" +edit-url-template = "https://github.com/ASIC-Network/analog-tutorials/edit/main/{path}" +additional-css = ["theme/asic-network.css"] +additional-js = ["theme/asic-network.js"] +site-url = "/analog-tutorials/" +input-404 = "404.md" +default-theme = "light" +preferred-dark-theme = "light" + +[output.html.playground] +copyable = true +editable = false +runnable = false + +[output.html.search] +enable = true +limit-results = 20 +use-boolean-and = true diff --git a/index.html b/index.html deleted file mode 100644 index c8deae2..0000000 --- a/index.html +++ /dev/null @@ -1,86 +0,0 @@ - - - - - -ASIC Network Docs - - - - - - - - - - - -
- - Loading… -
- -
- -
- - -
- -
-
-
-
-
-
-
- -
- - -
- - - - - - - - diff --git a/src/404.md b/src/404.md new file mode 100644 index 0000000..1f4ba1c --- /dev/null +++ b/src/404.md @@ -0,0 +1,4 @@ +# Page not found + +That page doesn't exist. Use the sidebar or [search](../) to find what you're +looking for. diff --git a/src/SUMMARY.md b/src/SUMMARY.md new file mode 100644 index 0000000..86c57d6 --- /dev/null +++ b/src/SUMMARY.md @@ -0,0 +1,6 @@ +# Summary + +[Welcome](./welcome.md) + +- [Inverter: Schematic to Layout](./inverter.md) +- [Ring Oscillator](./ring-oscillator.md) diff --git a/src/TEMPLATE.md b/src/TEMPLATE.md new file mode 100644 index 0000000..81d3351 --- /dev/null +++ b/src/TEMPLATE.md @@ -0,0 +1,54 @@ + + +# Project Title + +One or two sentences on what this page covers and what the reader will have working at the end. + +## Overview + +Context, theory, background. + +## Prerequisites + +- tool or PDK +- prior page: [example](other-page.md) + +## Steps + +1. first step +2. second step + +```bash +# commands go in fenced blocks with a language tag +echo "hello" +``` + +## Results + +| metric | value | +|--------|------:| +| example | 1.0 | + +## Checklist + +- [ ] item + +--- + +*Questions? Ask in the network Discord.* diff --git a/files/tutorial_files.md b/src/files/README.md similarity index 100% rename from files/tutorial_files.md rename to src/files/README.md diff --git a/Pages/img/ring_osc_schematic.png b/src/img/ring_osc_schematic.png similarity index 100% rename from Pages/img/ring_osc_schematic.png rename to src/img/ring_osc_schematic.png diff --git a/Pages/page_2.md b/src/inverter.md similarity index 99% rename from Pages/page_2.md rename to src/inverter.md index f4ee1a2..b1d004d 100644 --- a/Pages/page_2.md +++ b/src/inverter.md @@ -64,4 +64,4 @@ klayout inverter.gds - [ ] DC sweep shows correct switching behavior - [ ] Transient sim shows clean rise/fall - [ ] Layout matches schematic connectivity -- [ ] DRC clean \ No newline at end of file +- [ ] DRC clean diff --git a/Pages/page_3.md b/src/ring-oscillator.md similarity index 81% rename from Pages/page_3.md rename to src/ring-oscillator.md index befe477..4fbf672 100644 --- a/Pages/page_3.md +++ b/src/ring-oscillator.md @@ -1,12 +1,3 @@ - - # Ring Oscillator A 5-stage ring oscillator on SKY130, from schematic to simulated frequency. This page doubles as the **formatting reference**: every construct the renderer understands is used somewhere below. @@ -15,7 +6,7 @@ A 5-stage ring oscillator on SKY130, from schematic to simulated frequency. This ## Overview -The ring oscillator is the *hello world* of analog design: it needs correct device models, a working simulator, and nothing else. If this page simulates cleanly, your [toolchain setup](page_2.md) is good and every later tutorial will run. +The ring oscillator is the *hello world* of analog design: it needs correct device models, a working simulator, and nothing else. If this page simulates cleanly, your [toolchain setup](inverter.md) is good and every later tutorial will run. Inline styles for reference: **bold**, *italic*, ***both***, `inline code`, ~~struck~~, and an external link to the [SKY130 PDK docs](https://skywater-pdk.readthedocs.io). @@ -28,9 +19,8 @@ Inline styles for reference: **bold**, *italic*, ***both***, `inline code`, ~~st - Basic familiarity with SPICE netlists ## Schematic -![5-stage ring oscillator](img/ring_osc_schematic.png "Five inverters in a loop, buffered output") - +![5-stage ring oscillator](img/ring_osc_schematic.png "Five inverters in a loop, buffered output") Keep the loop symmetric: identical `W/L` on every stage, and buffer the tap so the probe capacitance does not load the ring. @@ -89,4 +79,4 @@ Frequency tracks `1/t_pd`, so expect roughly linear movement with VDD across cor - [SKY130 device documentation](https://skywater-pdk.readthedocs.io) - [ngspice manual](https://ngspice.sourceforge.io/docs.html) -- next up: [toolchain setup](page_2.md) +- next up: [toolchain setup](inverter.md) diff --git a/Pages/page_1.md b/src/welcome.md similarity index 89% rename from Pages/page_1.md rename to src/welcome.md index c6ad65d..ada60b6 100644 --- a/Pages/page_1.md +++ b/src/welcome.md @@ -1,12 +1,13 @@ -# Welcome to the analog-tutorials wiki! +# Welcome to the analog-tutorials wiki! + This wiki covers getting started with open-source analog IC design tools. The basic flow is [xschem](https://xschem.sourceforge.io/) for schematic capture, simulation, and SPICE netlist generation, then [KLayout](https://www.klayout.de/) for layout. Start by setting up the [IIC-OSIC-TOOLS](https://github.com/iic-jku/iic-osic-tools) container, following the [Setting Up Open Source Tools with Docker](https://kwantaekim.github.io/2024/05/25/OSE-Docker/) tutorial. ### Pages Once your environment is set up, work through the following examples, taking each block from schematic to layout: -1. [Inverter](https://asic-network.github.io/analog-tutorials/#page_2) -2. [Ring oscillator](https://asic-network.github.io/analog-tutorials/#page_3) +1. [Inverter](inverter.md) +2. [Ring oscillator](ring-oscillator.md) ### Literature recommendations * [Practical Electronics for Inventors](http://instrumentacion.qi.fcen.uba.ar/libro/Scherz.pdf) by Paul Scherz diff --git a/style.css b/style.css deleted file mode 100644 index 888b737..0000000 --- a/style.css +++ /dev/null @@ -1,157 +0,0 @@ -:root{ - --navy:#03418a; - --navy-2:#022f64; - --ink:#0a1f3d; - --blue:#2f6fd6; - --soft:#eaf0fb; - --trace:#c2d4f0; - --line:#e3e9f3; - --bg:#ffffff; - --bg-alt:#f7f9fd; - --text:#31415c; - --muted:#6a7a96; - --font:'Archivo',system-ui,sans-serif; - --mono:'IBM Plex Mono',ui-monospace,monospace; - --maxw:1200px; -} -*{box-sizing:border-box;margin:0;padding:0} -html{scroll-behavior:smooth} -body{font-family:var(--font);background:var(--bg);color:var(--text);line-height:1.6;-webkit-font-smoothing:antialiased} -img{max-width:100%;display:block} -button{font:inherit} - -/* ---------- nav ---------- */ -.nav{position:sticky;top:0;z-index:70;background:rgba(255,255,255,.86);backdrop-filter:blur(10px);border-bottom:1px solid var(--line)} -.nav .row{max-width:var(--maxw);margin:0 auto;padding:0 clamp(18px,4vw,32px);display:flex;align-items:center;gap:18px;height:64px} -.brand{display:flex;align-items:center;gap:11px;text-decoration:none;margin-right:auto} -.brand .chip{flex:none;transition:transform .3s ease} -.brand:hover .chip{transform:rotate(45deg)} -.chip .pins{stroke:var(--navy);stroke-width:2;stroke-linecap:round} -.chip .body{fill:var(--navy)} -.chip .core{fill:#fff;opacity:.92} -.brand .wm{font-weight:800;font-size:1.0rem;letter-spacing:.04em;color:var(--ink);line-height:1.15;display:block} -.brand .sub{font-family:var(--mono);font-size:.56rem;letter-spacing:.2em;color:var(--muted);text-transform:uppercase;display:block} -.links{display:flex;align-items:center;gap:4px} -.links a{position:relative;font-family:var(--mono);font-size:.72rem;letter-spacing:.1em;text-transform:uppercase;color:var(--text);text-decoration:none;padding:8px 11px;border-radius:7px;transition:color .15s} -.links a:hover{color:var(--navy)} -.links a::after{content:"";position:absolute;left:11px;right:11px;bottom:4px;height:1.5px;background:var(--navy);transform:scaleX(0);transform-origin:left;transition:transform .22s ease} -.links a:hover::after{transform:scaleX(1)} -.scrollbar{position:absolute;left:0;bottom:-1px;width:100%;height:2px;pointer-events:none} -.scrollbar span{display:block;height:100%;background:var(--navy);transform:scaleX(0);transform-origin:left} - -/* ---------- mobile toolbar ---------- */ -.toolbar{display:none;position:sticky;top:64px;z-index:60;background:rgba(255,255,255,.92);backdrop-filter:blur(8px);border-bottom:1px solid var(--line);padding:10px clamp(18px,4vw,32px);align-items:center;gap:12px} -.tb-btn{font-family:var(--mono);font-size:.7rem;letter-spacing:.1em;text-transform:uppercase;color:var(--navy);background:var(--soft);border:1px solid var(--trace);border-radius:8px;padding:8px 12px;cursor:pointer} -.tb-title{font-weight:600;font-size:.86rem;color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis} - -/* ---------- layout ---------- */ -.layout{display:grid;grid-template-columns:232px minmax(0,1fr) 196px;gap:clamp(24px,3.5vw,44px);padding:34px clamp(18px,4vw,32px) 70px;max-width:var(--maxw);margin:0 auto} -.sidebar h3,.toc h3{font-family:var(--mono);font-size:.64rem;font-weight:600;letter-spacing:.2em;text-transform:uppercase;color:var(--muted);margin-bottom:14px} -.sidebar{position:sticky;top:88px;align-self:start;max-height:calc(100vh - 112px);overflow:auto;padding-right:6px} -#page-nav{display:flex;flex-direction:column;gap:3px} -#page-nav a{display:flex;align-items:baseline;gap:10px;padding:8px 10px;border-radius:8px;text-decoration:none;color:var(--text);font-size:.87rem;font-weight:500;line-height:1.35;transition:background .14s,color .14s} -#page-nav a .pn{font-family:var(--mono);font-size:.58rem;font-weight:600;color:var(--navy);background:var(--soft);border:1px solid var(--trace);border-radius:4px;padding:1px 5px;flex:none} -#page-nav a:hover{background:var(--bg-alt);color:var(--ink)} -#page-nav a.active{background:var(--soft);color:var(--ink);font-weight:600;box-shadow:inset 2px 0 0 var(--navy)} -.side-note{font-family:var(--mono);font-size:.58rem;letter-spacing:.06em;line-height:1.8;color:var(--muted);border-top:1px dashed var(--line);margin-top:18px;padding-top:12px} - -.toc{position:sticky;top:88px;align-self:start;max-height:calc(100vh - 112px);overflow:auto} -#toc-nav{display:flex;flex-direction:column;gap:2px;border-left:1.5px solid var(--line)} -#toc-nav a{font-size:.78rem;color:var(--muted);text-decoration:none;padding:4px 0 4px 14px;margin-left:-1.5px;border-left:1.5px solid transparent;line-height:1.4;transition:color .14s,border-color .14s} -#toc-nav a.t-h3{padding-left:26px;font-size:.73rem} -#toc-nav a:hover{color:var(--ink)} -#toc-nav a.active{color:var(--navy);font-weight:600;border-left-color:var(--navy)} - -/* ---------- doc meta + pager ---------- */ -.doc-meta{display:flex;align-items:center;gap:14px;margin-bottom:22px;padding-bottom:14px;border-bottom:1px solid var(--line)} -.doc-meta .pgno{font-family:var(--mono);font-size:.62rem;font-weight:600;letter-spacing:.16em;text-transform:uppercase;color:var(--navy)} -.doc-meta .edit{margin-left:auto;font-family:var(--mono);font-size:.62rem;letter-spacing:.1em;text-transform:uppercase;color:var(--muted);text-decoration:none;transition:color .14s} -.doc-meta .edit:hover{color:var(--navy)} -.pager{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-top:52px} -.pg{display:flex;flex-direction:column;gap:6px;border:1px solid var(--line);border-radius:12px;padding:16px 18px;text-decoration:none;background:#fff;transition:transform .16s,border-color .16s,box-shadow .16s} -.pg:hover{transform:translateY(-2px);border-color:#cdd9ef;box-shadow:0 14px 30px -22px rgba(3,65,138,.45)} -.pg span{font-family:var(--mono);font-size:.6rem;letter-spacing:.14em;text-transform:uppercase;color:var(--muted)} -.pg b{color:var(--ink);font-size:.92rem;font-weight:700} -.pg.next{text-align:right;align-items:flex-end} - -/* ---------- markdown ---------- */ -.md{min-height:300px} -.md>:first-child{margin-top:0} -.md h1{font-weight:800;font-size:clamp(1.85rem,3.6vw,2.55rem);line-height:1.08;letter-spacing:-.02em;color:var(--ink);margin:0 0 .55em;padding-bottom:.45em;border-bottom:1px solid var(--line)} -.md h2{font-weight:800;font-size:1.42rem;letter-spacing:-.01em;color:var(--ink);margin:2em 0 .6em} -.md h3{font-weight:700;font-size:1.1rem;color:var(--ink);margin:1.7em 0 .5em} -.md h4{font-family:var(--mono);font-size:.72rem;font-weight:600;letter-spacing:.16em;text-transform:uppercase;color:var(--navy);margin:1.6em 0 .5em} -.md h5,.md h6{font-weight:700;font-size:.92rem;color:var(--ink);margin:1.4em 0 .4em} -.md :is(h1,h2,h3,h4,h5,h6){scroll-margin-top:96px} -.md .hlink{opacity:0;margin-left:9px;color:var(--trace);text-decoration:none;font-weight:600;border:0;transition:opacity .15s,color .15s} -.md :is(h1,h2,h3,h4,h5,h6):hover .hlink{opacity:1} -.md .hlink:hover{color:var(--navy)} -.md p{margin:.9em 0;line-height:1.78;font-size:.98rem} -.md a{color:var(--navy);text-decoration:none;border-bottom:1px solid var(--trace);transition:border-color .14s} -.md a:hover{border-bottom-color:var(--navy)} -.md strong{color:var(--ink);font-weight:700} -.md del{color:var(--muted)} -.md code{font-family:var(--mono);font-size:.84em;background:var(--soft);border:1px solid var(--trace);border-radius:5px;padding:.08em .38em;color:var(--navy-2)} -.md ul,.md ol{margin:.9em 0;padding-left:1.45em} -.md li{margin:.4em 0;line-height:1.7;font-size:.98rem} -.md li::marker{color:var(--navy);font-weight:600} -.md li.task{list-style:none;margin-left:-1.45em;display:flex;align-items:baseline;gap:9px} -.md li.task input{accent-color:var(--navy);transform:translateY(1px)} -.md ul ul,.md ol ol,.md ul ol,.md ol ul{margin:.35em 0} -.md blockquote{margin:1.4em 0;padding:6px 20px;border-left:3px solid var(--navy);background:var(--soft);border-radius:0 12px 12px 0} -.md blockquote p{font-size:.94rem} -.md hr{border:0;height:1px;background:linear-gradient(90deg,var(--line),transparent);margin:2.4em 0} -.codeblock{margin:1.5em 0;border:1px solid var(--line);border-radius:12px;overflow:hidden;background:#fff} -.codeblock figcaption{display:flex;align-items:center;gap:9px;padding:9px 14px;background:var(--soft);border-bottom:1px solid var(--line)} -.codeblock .dot{width:7px;height:7px;border-radius:50%;background:var(--navy);animation:dotpulse 2.4s ease-in-out infinite} -.codeblock .lang{font-family:var(--mono);font-size:.6rem;font-weight:600;letter-spacing:.16em;text-transform:uppercase;color:var(--navy)} -.codeblock .copy{margin-left:auto;font-family:var(--mono);font-size:.58rem;letter-spacing:.12em;text-transform:uppercase;color:var(--muted);background:#fff;border:1px solid var(--line);border-radius:6px;padding:4px 9px;cursor:pointer;transition:color .14s,border-color .14s} -.codeblock .copy:hover{color:var(--navy);border-color:var(--trace)} -.codeblock pre{padding:16px 18px;overflow:auto;background:#fbfcff} -.codeblock pre code{font-family:var(--mono);font-size:.82rem;line-height:1.7;color:var(--ink);background:none;border:0;padding:0} -.tablewrap{margin:1.5em 0;border:1px solid var(--line);border-radius:12px;overflow-x:auto} -.md table{width:100%;border-collapse:collapse;font-size:.9rem} -.md th{font-family:var(--mono);font-size:.62rem;font-weight:600;letter-spacing:.14em;text-transform:uppercase;color:var(--navy);background:var(--soft);padding:11px 15px;text-align:left;border-bottom:1px solid var(--line);white-space:nowrap} -.md td{padding:10px 15px;border-bottom:1px solid var(--line);line-height:1.6} -.md tbody tr:last-child td{border-bottom:0} -.md tbody tr:nth-child(even){background:#fafcff} -figure.img{margin:1.7em 0} -figure.img img{border:1px solid var(--line);border-radius:12px;box-shadow:0 18px 40px -30px rgba(3,65,138,.4)} -figure.img figcaption{font-family:var(--mono);font-size:.6rem;letter-spacing:.12em;text-transform:uppercase;color:var(--muted);margin-top:9px} - -/* ---------- states ---------- */ -@keyframes skshimmer{0%{background-position:100% 0}100%{background-position:0 0}} -.sk{height:12px;border-radius:6px;background:linear-gradient(90deg,var(--soft) 25%,#f1f4fb 37%,var(--soft) 63%);background-size:400% 100%;animation:skshimmer 1.4s ease-in-out infinite;margin:10px 0} -.state-card{border:1px dashed var(--trace);border-radius:14px;padding:30px;background:var(--bg-alt)} -.state-card h2{border:0;padding:0;margin:0 0 10px;font-size:1.15rem} -.state-card p{font-size:.9rem;color:var(--muted)} -@keyframes dotpulse{0%,100%{box-shadow:0 0 0 3px rgba(3,65,138,.16)}50%{box-shadow:0 0 0 7px rgba(3,65,138,.04)}} - -/* ---------- footer ---------- */ -footer{border-top:1px solid var(--line);padding:26px 0;background:var(--bg-alt)} -footer .row{max-width:var(--maxw);margin:0 auto;padding:0 clamp(18px,4vw,32px);display:flex;align-items:center;gap:12px;flex-wrap:wrap} -footer .wm{font-weight:800;color:var(--ink);letter-spacing:.03em;font-size:.92rem} -footer .tag{font-family:var(--mono);font-size:.66rem;letter-spacing:.12em;text-transform:uppercase;color:var(--muted)} -footer a{margin-left:auto;color:var(--text);text-decoration:none;font-family:var(--mono);font-size:.68rem;letter-spacing:.1em;text-transform:uppercase;transition:color .14s} -footer a:hover{color:var(--navy)} - -/* ---------- drawer ---------- */ -.backdrop{position:fixed;inset:0;background:rgba(10,31,61,.35);z-index:64;opacity:0;pointer-events:none;transition:opacity .2s} -body.drawer-open .backdrop{opacity:1;pointer-events:auto} - -@media(max-width:1120px){ - .toc{display:none} - .layout{grid-template-columns:232px minmax(0,1fr)} -} -@media(max-width:880px){ - .layout{grid-template-columns:1fr;padding-top:24px} - .toolbar{display:flex} - .sidebar{position:fixed;top:64px;bottom:0;left:0;width:min(310px,85vw);background:#fff;z-index:66;padding:22px 20px;transform:translateX(-103%);transition:transform .24s ease;border-right:1px solid var(--line);max-height:none;box-shadow:0 20px 60px rgba(3,65,138,.25)} - body.drawer-open .sidebar{transform:none} - .pager{grid-template-columns:1fr} - .pg.next{text-align:left;align-items:flex-start} -} -@media(prefers-reduced-motion:reduce){ - html{scroll-behavior:auto} - *,*::before,*::after{animation:none!important;transition:none!important} -} diff --git a/theme b/theme new file mode 160000 index 0000000..ffcb5a0 --- /dev/null +++ b/theme @@ -0,0 +1 @@ +Subproject commit ffcb5a0e004359f23187e41c3f5e06c02eaeb80a