<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="https://umn0mtkzgkj46tygt32g.julianrbryant.com/2005/Atom" xml:base="https://un5nvy3mxv5kcnr.julianrbryant.com/notes/">
  <id>https://un5nvy3mxv5kcnr.julianrbryant.com/notes/</id>
  <title>lottia notes</title>
  <updated>2025-07-26T05:10:00Z</updated>
  <link rel="alternate" href="https://un5nvy3mxv5kcnr.julianrbryant.com/notes/" type="text/html"/>
  <link rel="self" href="https://un5nvy3mxv5kcnr.julianrbryant.com/notes/atom.xml" type="application/atom+xml"/>
  <author>
    <name>Charlotte</name>
    <uri>https://un5nvy3mxv5kcnr.julianrbryant.com/notes</uri>
  </author>
  <entry>
    <id>tag:lottia.net,2025-07-26:/notes/0017-fortune-habits-in-nix.html</id>
    <title type="html">Fortune habits in Nix</title>
    <published>2025-07-26T05:10:00Z</published>
    <updated>2025-07-26T05:10:00Z</updated>
    <link rel="alternate" href="https://un5nvy3mxv5kcnr.julianrbryant.com/notes/0017-fortune-habits-in-nix.html" type="text/html"/>
    <content type="html">&lt;section id="top"&gt;
&lt;p&gt;I recently saw and liked &lt;a href="https://un5gmtkzghdxewn6p68ar9hckfjg.julianrbryant.com/blog/using-fortune-to-reinforce-habits/"&gt;Judy2k’s “Using fortune to reinforce
habits”&lt;/a&gt;
— building effective habits is an under-appreciated part of learning any
discipline.  (And &lt;code&gt;xh&lt;/code&gt; is one I had the &lt;a href="https://un5zt09ugh70.julianrbryant.com/~talya/vyx/blob/main/c008ed3595836a4984aa9075ef4b478094bb527f/home.nix#L54"&gt;exact same trouble with&lt;/a&gt;.)  Let’s
implement it in our Nix setup.&lt;/p&gt;
&lt;h2&gt;Step 0: don’t install &lt;code&gt;fortune&lt;/code&gt;
&lt;/h2&gt;
&lt;p&gt;You don’t have to do this.  That’s probably one of the neatest parts of Nix
right there — you can have &lt;code&gt;fortune&lt;/code&gt; embedded in your daily workflow &lt;em&gt;without&lt;/em&gt;
having to expose it globally, without accidentally tab-completing &lt;code&gt;strfile&lt;/code&gt; or
&lt;code&gt;rot&lt;/code&gt; when working and having no idea why that’s a thing.&lt;/p&gt;
&lt;h2&gt;Step 1: write your habits file&lt;/h2&gt;
&lt;p&gt;This file goes into your Nix configuration, whether it’s a nix-darwin flake or a
vanilla NixOS install with all your config in &lt;code&gt;/etc/nixos&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Write your lines, with &lt;code&gt;%&lt;/code&gt; separating entries.  Don’t run &lt;code&gt;strfile&lt;/code&gt; on it —
remember, we didn’t put &lt;code&gt;fortune&lt;/code&gt; in our PATH, and we don’t need to do that
work, nor do we want to add a manual step (running &lt;code&gt;strfile&lt;/code&gt;) for us to forget
later.&lt;/p&gt;
&lt;p&gt;I’m assuming it’s called &lt;code&gt;habits&lt;/code&gt; hereon.  Here’s what &lt;a href="https://un5zt09ugh70.julianrbryant.com/~talya/vyx/blob/main/9fa6d6ee497749bc119860292ed58704e6b4ec45/home/habits"&gt;my first version&lt;/a&gt; of
&lt;code&gt;habits&lt;/code&gt; looks like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mtr for traceroute.
%
entr to re-run on file change.
%
xh is our HTTPie replacement!
%
fd [pattern] [path ...]
%
Try using gg for jj.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Start small.  Once you’re seeing these regularly, you’ll also remember where to
add new ones when they occur to you!&lt;/p&gt;
&lt;h2&gt;Step 2: write a derivation that compiles your habits file&lt;/h2&gt;
&lt;p&gt;We want to run &lt;code&gt;fortune&lt;/code&gt;’s &lt;code&gt;strfile&lt;/code&gt; on the habits file.  We need to store the
result side-by-side with the input; the output of &lt;code&gt;strfile&lt;/code&gt; is just an index
into the original.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-nix"&gt;&lt;span class="kd"&gt;let&lt;/span&gt;
  &lt;span class="kn"&gt;inherit&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;pkgs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;fortune&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;in&lt;/span&gt;
&lt;span class="nv"&gt;pkgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;stdenv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;mkDerivation&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"vyx-habits"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nv"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sx"&gt;./habits&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nv"&gt;unpackPhase&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;''&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;    true&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;  ''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nv"&gt;buildInputs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;fortune&lt;/span&gt; &lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="nv"&gt;buildPhase&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;''&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;    strfile $src habits.dat&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;  ''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nv"&gt;installPhase&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;''&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;    mkdir -p $out&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;    cp $src $out/habits&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;    cp habits.dat $out/habits.dat&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;  ''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nv"&gt;passthru&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;inherit&lt;/span&gt; &lt;span class="nv"&gt;fortune&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I put this in config evaluated by Home Manager, but you can put it anywhere you
have access to Nixpkgs.  Let’s go through it step-by-step.&lt;/p&gt;
&lt;p&gt;First, we bring &lt;code&gt;fortune&lt;/code&gt; in scope.  The &lt;a href="https://un5h8u57gjpbbbnrx28f6wr.julianrbryant.com/packages?channel=25.05&amp;amp;show=fortune&amp;amp;from=0&amp;amp;size=50&amp;amp;sort=relevance&amp;amp;type=packages&amp;amp;query=fortune-mod"&gt;package in Nixpkgs&lt;/a&gt; has &lt;code&gt;fortune&lt;/code&gt; and
&lt;code&gt;strfile&lt;/code&gt; in its &lt;code&gt;bin&lt;/code&gt; output.&lt;/p&gt;
&lt;p&gt;Now we create a derivation.  We include the habits file we wrote as the &lt;code&gt;src&lt;/code&gt;
attribute, and replace the &lt;code&gt;unpackPhase&lt;/code&gt; with a no-op since there’s nothing
to extract.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;fortune&lt;/code&gt; is the lone build input, meaning that its &lt;code&gt;bin&lt;/code&gt; outputs (including
&lt;code&gt;strfile&lt;/code&gt;) will be in &lt;code&gt;$PATH&lt;/code&gt;.  To build, we run &lt;code&gt;strfile&lt;/code&gt; on the source. The
source will be in the Nix store, so we supply an output filename — by default,
&lt;code&gt;strfile&lt;/code&gt; will try to write its output next to the input.&lt;/p&gt;
&lt;p&gt;To install, we create the output directory (this is a Nix-ism you’ll see
everywhere), and then copy both the input file and the compiled index there.&lt;/p&gt;
&lt;p&gt;Finally, we pass through the &lt;code&gt;fortune&lt;/code&gt; package we used as an attribute on this
derivation itself.  We’ll use this to actually call &lt;code&gt;fortune&lt;/code&gt; later.&lt;/p&gt;
&lt;p&gt;Lovely!  We now have a derivation that indexes our habits file, and puts the
result somewhere that we can pass to &lt;code&gt;fortune&lt;/code&gt; and have it Just Work(tm).&lt;/p&gt;
&lt;h2&gt;Step 3: integrate with your shell&lt;/h2&gt;
&lt;p&gt;This will vary depending on your shell setup.  I’ll show how it works with mine.&lt;/p&gt;
&lt;p&gt;I configure fish using Home Manager.  One of the configuration
options is &lt;a href="https://un5m4by44v890yaytppvewt5eymc0hp3.julianrbryant.com/home-manager/options.xhtml#opt-programs.fish.interactiveShellInit"&gt;&lt;code&gt;programs.fish.interactiveShellInit&lt;/code&gt;&lt;/a&gt;, which is shell
code called when an interactive shell is initialising.  I set the
&lt;code&gt;fish_greeting&lt;/code&gt; variable here, which the default &lt;a href="https://un5pe0hcw2ujba8.julianrbryant.com/docs/current/cmds/fish_greeting.html"&gt;&lt;code&gt;fish_greeting&lt;/code&gt;
function&lt;/a&gt; just
displays.&lt;/p&gt;
&lt;p&gt;I bind the above derivation to the name &lt;code&gt;habits&lt;/code&gt;, and then set &lt;code&gt;fish_greeting&lt;/code&gt;
by invoking the passed-through &lt;code&gt;fortune&lt;/code&gt; package, with the argument being the
base name of the complied &lt;code&gt;habits&lt;/code&gt;.  (&lt;code&gt;fortune&lt;/code&gt; will look for the &lt;code&gt;.dat&lt;/code&gt; file
next to it.)&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-nix"&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;habits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt;
    &lt;span class="kn"&gt;inherit&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;pkgs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;fortune&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kn"&gt;in&lt;/span&gt;
  &lt;span class="nv"&gt;pkgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;stdenv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;mkDerivation&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;# ... (unchanged from above) ...&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="kn"&gt;in&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;programs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;fish&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;interactiveShellInit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;''&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;      # ... (elided) ...&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;      set fish_greeting 'Nyonk! '(&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;habits&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;fortune&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/bin/fortune &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;habits&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/habits)&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;    ''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note how our use of the &lt;code&gt;passthru&lt;/code&gt; attribute means we don’t actually have the
&lt;code&gt;fortune&lt;/code&gt; package in scope anywhere except building the derivation itself; this
is a nice kind of clean, and means there’s no chance of e.g. using a different
&lt;code&gt;fortune&lt;/code&gt; to compile the index than what we use to read it.  (There’s probably
very little chance of a breaking change here between &lt;code&gt;fortune&lt;/code&gt; versions, lol,
but imagine (much) bigger systems and you can see how this could be handy. :))&lt;/p&gt;
&lt;h2&gt;Step 4: prophet&lt;/h2&gt;
&lt;p&gt;Yay, we’re done!&lt;/p&gt;
&lt;p&gt;Now, whenever you modify the content of &lt;code&gt;habits&lt;/code&gt; in your configuration, a new
derivation will be built, and your shell init will use it.&lt;/p&gt;
&lt;p&gt;Let’s have a look at what our built fish config looks like:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-console"&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;grep &lt;/span&gt;Nyonk ~/.config/fish/config.fish
&lt;span class="go"&gt;set fish_greeting 'Nyonk! '(/nix/store/543q6d77f4p27572xb9c5wngg7mg5rh8-fortune-mod-3.24.0/bin/fortune /nix/store/ggp28h3cmvciyaxfr0rxaz154ljap89l-vyx-habits/habits)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;oh yeah i love horizontal scrolling.  And what does our derivation’s output
directory look like?&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-console"&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; /nix/store/ggp28h3cmvciyaxfr0rxaz154ljap89l-vyx-habits
&lt;span class="go"&gt;total 8
-r--r--r-- 1 root wheel 134 Jan  1  1970 habits
-r--r--r-- 1 root wheel  48 Jan  1  1970 habits.dat&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Neat!&lt;/p&gt;
&lt;h2&gt;Step 5: next steps&lt;/h2&gt;
&lt;p&gt;You could try some of these:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Instead of just packaging the habits, &lt;a href="https://un5m4by4xjqx6zm5.julianrbryant.com/manual/nixpkgs/stable/#trivial-builder-writeShellScriptBin"&gt;write out a script&lt;/a&gt; that actually calls
&lt;code&gt;fortune&lt;/code&gt; with the supplied data.  Then using it is just a matter of invoking
your package’s &lt;code&gt;bin&lt;/code&gt; output.&lt;/li&gt;
&lt;li&gt;Alternatively, put together the calling syntax in a &lt;code&gt;passthru&lt;/code&gt; attribute, and
just interpolate that into your shell init.  Shell-specific, but kinda neat.&lt;/li&gt;
&lt;li&gt;Want some experience writing NixOS modules?  You could write a module which
builds this and injects it into your shell config with a single &lt;code&gt;enable = true;&lt;/code&gt;.  Bonus points for accepting the habits data as a configuration option!&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
</content>
  </entry>
  <entry>
    <id>tag:lottia.net,2025-01-06:/notes/0016-textmode-designer.html</id>
    <title type="html">TextMode Designer</title>
    <published>2025-01-06T01:58:00Z</published>
    <updated>2025-01-06T02:44:00Z</updated>
    <link rel="alternate" href="https://un5nvy3mxv5kcnr.julianrbryant.com/notes/0016-textmode-designer.html" type="text/html"/>
    <content type="html">&lt;section id="top"&gt;
&lt;p&gt;Here’s a video of the TextMode Designer in use, fixing the control order of a
dialog used in the &lt;a href="https://github.com/charlottia/ava/tree/main/adc"&gt;ADC&lt;/a&gt;, and one of designing a new dialog.&lt;/p&gt;
&lt;p&gt;&lt;video style="width: 100%" controls=""&gt;&lt;source src="assets/reorder.mp4" type="video/mp4"&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;&lt;video style="width: 100%" controls=""&gt;&lt;source src="assets/design.mp4" type="video/mp4"&gt;&lt;/video&gt;&lt;/p&gt;
&lt;/section&gt;
</content>
  </entry>
  <entry>
    <id>tag:lottia.net,2024-12-17:/notes/0015-comptime-flow-exhaustiveness.html</id>
    <title type="html">Comptime, flow, and exhaustiveness</title>
    <published>2024-12-17T10:02:36Z</published>
    <updated>2024-12-17T10:02:36Z</updated>
    <link rel="alternate" href="https://un5nvy3mxv5kcnr.julianrbryant.com/notes/0015-comptime-flow-exhaustiveness.html" type="text/html"/>
    <content type="html">&lt;section id="top"&gt;
&lt;p&gt;Busy working on the &lt;a href="https://github.com/charlottia/ava/tree/main/adc"&gt;ADC&lt;/a&gt; lately, but I just happened upon this
kind-of-follow-up to the &lt;a href="https://un5nvy3mxv5kcnr.julianrbryant.com/notes/0011-non-intrusive-vtable.html"&gt;non-intrusive vtable&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I have methods implemented by only some “subtypes”, and usually “do nothing” is
the correct default, or perhaps “return &lt;code&gt;null&lt;/code&gt;”. They end up looking like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-zig"&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;handleMouseDown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Control&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SDL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;MouseButton&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;clicks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;cm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Allocator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;!?&lt;/span&gt;&lt;span class="n"&gt;Control&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;inline&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;@hasDecl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;@TypeOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.*&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;"handleMouseDown"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;handleMouseDown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;clicks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cm&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;handleMouseDrag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Control&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SDL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;MouseButton&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;inline&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;@hasDecl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;@TypeOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.*&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;"handleMouseDrag"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;handleMouseDrag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;’tis a fine barn, but sure ’tis no pool.&lt;/p&gt;
&lt;p&gt;What I would keep encountering was that I’d write an implementation for a
“““““subtype”””””, and then run the program and wonder why the behaviour seemed
unchanged. It’s amazing how many times you can encounter the exact same problem
— in this case, a missing &lt;code&gt;pub&lt;/code&gt; qualifier on the implementations, meaning
they’re invisible to other files.&lt;/p&gt;
&lt;p&gt;What if we made the default behaviour opt-in, instead of implicit? Here’s an
example:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-zig"&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Control&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="n"&gt;Control&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;inline&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;@hasDecl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;@TypeOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.*&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;"parent"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;@hasField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;@TypeOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.*&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;"orphan"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;orphan&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We check if there’s a &lt;code&gt;parent&lt;/code&gt; decl, and call it if so. If not, we check for an
&lt;code&gt;orphan&lt;/code&gt; field, and if it exists and is true, do our default action. Note that
we don’t &lt;em&gt;assert&lt;/em&gt; this as the only other alternative. Let’s see what happens if
we compile an existing control that doesn’t supply either:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;src/Imtui.zig:63:30: error: function with non-void return type '?Imtui.Control' implicitly returns
    fn parent(self: Control) ?Control {
                             ^~~~~~~~
src/Imtui.zig:71:5: note: control flow reaches end of body here
    }
    ^
referenced by:
    focus__anon_8053: src/Imtui.zig:352:35
    accelerate: src/controls/DialogButton.zig:67:29
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The function implicitly returns! Both conditions evaluate to false at comptime,
so the body of the method ends up being totally empty. (Alternatively, if
you use &lt;code&gt;return switch&lt;/code&gt;, you’ll see a message about &lt;code&gt;error: expected type 'whatever', found 'void'&lt;/code&gt;.)&lt;/p&gt;
&lt;p&gt;It’s not very helpful, because the reference trace refers to the point at
which this function gets &lt;em&gt;called&lt;/em&gt;. We don’t actually know which is the missing
implementation, just that it exists.&lt;/p&gt;
&lt;p&gt;But that’s okay, we can add that ourselves!&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-zig"&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Control&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="n"&gt;Control&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;inline&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;@hasDecl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;@TypeOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.*&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;"parent"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;@hasField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;@TypeOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.*&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;"orphan"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;orphan&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nb"&gt;@compileError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;@typeName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;@TypeOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.*&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="s"&gt;" doesn't implement parent or set orphan"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ja nii:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;src/Imtui.zig:70:17: error: controls.Dialog.Impl doesn't implement parent or set orphan
                @compileError(@typeName(@TypeOf(c.*)) ++ " doesn't implement parent or set orphan");
                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
referenced by:
    focus__anon_8053: src/Imtui.zig:354:35
    accelerate: src/controls/DialogButton.zig:67:29
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, note how a field used this way must be: &lt;code&gt;comptime&lt;/code&gt;!&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-zig"&gt;&lt;span class="k"&gt;comptime&lt;/span&gt; &lt;span class="n"&gt;orphan&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;bool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If not, its value won’t be available at comptime, and codegen will need to
produce a runtime condition for the &lt;code&gt;c.orphan&lt;/code&gt; check, meaning the possibility of
false is always entertained and the &lt;code&gt;@compileError&lt;/code&gt; will fire.&lt;/p&gt;
&lt;p&gt;Things to consider:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It might be worth asserting in the first branch that &lt;code&gt;orphan&lt;/code&gt; isn’t set to
true, to avoid any confusion about behaviour when both are set.&lt;/li&gt;
&lt;li&gt;We only got the exhaustiveness thing because this example returns a value.
With &lt;code&gt;void&lt;/code&gt; returns, the &lt;code&gt;@compileError&lt;/code&gt; isn’t optional if you want to know if
you forgot.&lt;/li&gt;
&lt;li&gt;Did you buy the graphite tube?&lt;/li&gt;
&lt;li&gt;Try &lt;code&gt;comptime opaque: void&lt;/code&gt; to avoid the need for &lt;code&gt;@hasDecl()&lt;/code&gt; &lt;em&gt;and&lt;/em&gt; the
boolean check! Does it work? Almost like little tags, attributes, hmmmmmm.&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
</content>
  </entry>
  <entry>
    <id>tag:lottia.net,2024-11-24:/notes/0014-hidpi-platform-specific-hacks.html</id>
    <title type="html">Platform-specific hacks for high-DPI</title>
    <published>2024-11-24T11:06:00Z</published>
    <updated>2024-11-24T11:06:00Z</updated>
    <link rel="alternate" href="https://un5nvy3mxv5kcnr.julianrbryant.com/notes/0014-hidpi-platform-specific-hacks.html" type="text/html"/>
    <content type="html">&lt;section id="top"&gt;
&lt;p&gt;This was written up while working on &lt;a href="https://github.com/charlottia/ava"&gt;Ava BASIC&lt;/a&gt;’s IDE, the &lt;a href="https://github.com/charlottia/ava/tree/main/adc"&gt;Amateur
Development Client (ADC)&lt;/a&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="macos"&gt;
&lt;h2&gt;macOS &lt;a href="#macos" aria-hidden="true" title="Permalink to section" class="anchor"&gt;🔗&lt;/a&gt; &lt;a href="#top" aria-hidden="true" title="Back to top" class="anchor"&gt;↩&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;We set &lt;code&gt;.allow_high_dpi = true&lt;/code&gt; when creating the window with SDL, which does
the right thing on macOS.  We classify this situation as “native hidpi”; we
recognise it by noting the created renderer has an output size 2x that of the
window in both dimenisons.
&lt;ul&gt;
&lt;li&gt;We only need set the renderer scale factor to 2; everything else behaves
as if it’s at 1x automagically.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SDL_GetDisplayDPI&lt;/code&gt; always returns the native DPI for the display.
&lt;ul&gt;
&lt;li&gt;On my Macbook’s 14” Retina screen @ 3024 x 1964, this is around 255x255.&lt;/li&gt;
&lt;li&gt;On a 32” 4K monitor, this is 137x137.&lt;/li&gt;
&lt;li&gt;This appears reconstituted from other figures as it varies with
floating-point tendencies between resolutions, but it’s about right.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id="windows"&gt;
&lt;h2&gt;Windows &lt;a href="#windows" aria-hidden="true" title="Permalink to section" class="anchor"&gt;🔗&lt;/a&gt; &lt;a href="#top" aria-hidden="true" title="Back to top" class="anchor"&gt;↩&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;SDL’s &lt;code&gt;.allow_high_dpi = true&lt;/code&gt; doesn’t do anything.&lt;/li&gt;
&lt;li&gt;We use the &lt;a href="https://un5hru1qgj43w9rdtvyj8.julianrbryant.com/en-us/windows/win32/api/winuser/nf-winuser-setprocessdpiaware"&gt;&lt;code&gt;SetProcessDPIAware&lt;/code&gt;&lt;/a&gt; Win32 call so we don’t get affected by
Windows’ automatic UI scaling.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SDL_GetDisplayDPI&lt;/code&gt; seems to give &lt;code&gt;96 * ui_scaling_factor&lt;/code&gt;, so we get 144x144
with the default 150% UI scaling on piret, and not 125x125 like we actually
have, but it’s good enough.&lt;/li&gt;
&lt;li&gt;We then apply “manual hidpi” (see Linux).&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id="linux"&gt;
&lt;h2&gt;Linux &lt;a href="#linux" aria-hidden="true" title="Permalink to section" class="anchor"&gt;🔗&lt;/a&gt; &lt;a href="#top" aria-hidden="true" title="Back to top" class="anchor"&gt;↩&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;SDL’s &lt;code&gt;.allow_high_dpi = true&lt;/code&gt; doesn’t do anything.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SDL_GetDisplayDPI&lt;/code&gt; always returns the native resolution DPI for the display,
on both X and Wayland.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Note: Trying to get the acutal DPI/scaling factor in use seems Really Hard™.
I actually didn’t even bother trying at all at first, and jumped straight to
the current solution, but after I ironed out all three I thought, “wouldn’t
it be nice?” Anyway: hahahahahahahahaha. I’m sure there’s a nice way to do it.
(hahahahahahaha.)&lt;/p&gt;
&lt;p&gt;At some point I started feeling really committed to writing this note, so let’s
actually research this properly. The first obvious bifurcation is X vs Wayland.
Going any lower than that is a path to madness (i.e. code specific to e.g. KDE,
your particular Wayland compositor, etc.) and frankly my life is full of those
as it is.&lt;/p&gt;
&lt;h3&gt;X&lt;/h3&gt;
&lt;p&gt;Let’s try to get an answer for X first. I normally use Wayland — on booting
my machine into X instead (still Plasma 6), I’m greeted with my UI entirely
unscaled! I’m surprised. Let’s investigate ways to obtain a figure here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;xrdb -query&lt;/code&gt; doesn’t have an &lt;code&gt;Xft.dpi&lt;/code&gt; entry.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;xrandr&lt;/code&gt; gives 2560x1600, and the monitor’s size as 345mm x 215mm (which is
correct, and gives us a PPI of 188x189).  It doesn’t hazard an attempt at
giving any UI scaling factor or DPI setting (despite accepting one with
&lt;code&gt;xrandr --dpi&lt;/code&gt;? what must that actually do?).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;xdpyinfo&lt;/code&gt; (&lt;code&gt;nixpkgs#xorg.xdpyinfo&lt;/code&gt;) gives 2560x1600 at 96 dpi, and notes the
screen dimensions as 677mm x 423mm, which appears to be calculated simply from
the previous figures.&lt;/li&gt;
&lt;li&gt;Some folks have suggested grepping X’s logs! Where found, it said 96. (Can you
imagine if this just had The Answer? “Please specify path to your X server’s
logfile or supply systemd unit name to continue.”)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I reset the UI scaling factor (to 150%), and then restarted X (without which
about half the items had it applied, half not).&lt;/p&gt;
&lt;p&gt;Only &lt;code&gt;xrdb -query&lt;/code&gt;’s output changed: &lt;code&gt;Xft.dpi: 144&lt;/code&gt; has appeared.  This looks
useful — it looks like the &lt;code&gt;96 * ui_scaling_factor&lt;/code&gt; thing as well here.
Let’s sigh and verify by checking 125% (it writes, having already gone back
into Wayland … ugh. The worst part is the keyboard (I typo my password on it
far too often; not used to the Framework), and &lt;a href="https://github.com/NixOS/nixpkgs/issues/239770#issuecomment-1868508908"&gt;a bug&lt;/a&gt; somewhere between KDE,
SDDM and the laptop’s fingerprint reader means logins take about 30 seconds
to process, and honestly I’m just one cat up against the world here).  Yes!
&lt;code&gt;Xft.dpi: 120&lt;/code&gt;!&lt;/p&gt;
&lt;p&gt;Overall, there’s not a lot; it is clear that most desktop environment toolkits
implement this themselves, and so there’s not necessarily a straight answer.
Querying the font DPI X resource seems likely to give a useful number, though,
when present, and I think we can call that Good Enough™. We have to shell out,
which is ugly as hell, or query the X server ourselves.&lt;/p&gt;
&lt;h3&gt;Wayland&lt;/h3&gt;
&lt;p&gt;First up, let’s try all the X methods against Xwayland, just in case we get an
easy win. This is at 150%.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;xrdb -query&lt;/code&gt; gives &lt;code&gt;Xft.dpi: 144&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;xrandr&lt;/code&gt; gives the same as real X. (Not the actual same; a lot is different
thanks to the virtual server. But the relevant stuff is identical.)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;xdpyinfo&lt;/code&gt; does likewise.&lt;/li&gt;
&lt;li&gt;There’s a lot less in the logs/journals for Wayland that I’ve found.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;What about 125%? &lt;code&gt;xrdb -query&lt;/code&gt; gives &lt;code&gt;Xft.dpi: 120&lt;/code&gt;, rest the same. 100%?
&lt;code&gt;Xft.dpi: 96&lt;/code&gt;, the first time we see this result explicitly here.&lt;/p&gt;
&lt;p&gt;And it turns out, that’s it: that’s the solution. We can get some minimally
useful information for both display managers with the one method.&lt;/p&gt;
&lt;p&gt;… but if we did want more information from Wayland, does its design mean
we can get it? Turns out, yes.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;wayland-info&lt;/code&gt; (&lt;code&gt;nixpkgs#wayland-utils&lt;/code&gt;) gives us:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;scaling factor of &lt;code&gt;2&lt;/code&gt; in &lt;code&gt;wl_output&lt;/code&gt; interface properties; and&lt;/li&gt;
&lt;li&gt;logical display size of 1707x1067 from &lt;code&gt;zxdg_output_manager_v1&lt;/code&gt; interface, but
maybe that’s not (as) reliable.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;code&gt;2&lt;/code&gt; there is an interesting one. It’s &lt;code&gt;1&lt;/code&gt; at 100%, but &lt;code&gt;2&lt;/code&gt;
above that. Above 200% or so it seems to go to &lt;code&gt;3&lt;/code&gt;. This is
&lt;a href="https://docs.rs/wayland-server/latest/wayland_server/protocol/wl_output/struct.WlOutput.html#method.scale"&gt;&lt;code&gt;wayland_server::protocol::wl_output::WlOutput::scale&lt;/code&gt;&lt;/a&gt;; I guess it (?
Plasma?) gets us 150% by rendering at 200%, and then bitmap-scaling the result?
That doesn’t sound right. &lt;a href="https://un5n60am8zrtp1ygv78wpvjg1cf0.julianrbryant.com/blog/posts/2022-06-10-wayland-xorg/wayland-xorg.html"&gt;Oh boy&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;zxdg_output_manager_v1&lt;/code&gt; figure gives us exactly what we want, in
&lt;a href="https://wayland.app/protocols/xdg-output-unstable-v1#zxdg_output_v1:event:logical_size"&gt;&lt;code&gt;zxdg_output_v1::logical_size&lt;/code&gt;&lt;/a&gt;, but it looks comparatively new/
unstable and isn’t a core part of the protocol, so I assume it’s comparatively
unreliable. (Using that we can calculate the actual set DPI, i.e. 125x125.)&lt;/p&gt;
&lt;p&gt;I guess on Wayland (with Xwayland) the perfect priority would be
&lt;code&gt;zxdg_output_v1::logical_size&lt;/code&gt; &amp;gt; &lt;code&gt;xrdb -query&lt;/code&gt; &amp;gt; &lt;code&gt;wl_output::scale&lt;/code&gt;. But I’m going
to assume Xwayland is in fact ubiquitous, and therefore &lt;code&gt;xrdb -query&lt;/code&gt; will have
to do for both.&lt;/p&gt;
&lt;p&gt;(If I actually end up implementing this, I’m probably going to eventually
succumb and implement the X calls to avoid the subprocess.)&lt;/p&gt;
&lt;h3&gt;Okay, but what is the actual solution?!&lt;/h3&gt;
&lt;p&gt;When the reported DPI is greater than or equal to 100 in either dimension,
set the renderer scale to 2, double both the window dimensions, and set the
effective scale to 2.  The effective scale is divided from cursor positions in
mouse events before they’re handled.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="wtf"&gt;
&lt;h2&gt;wtf &lt;a href="#wtf" aria-hidden="true" title="Permalink to section" class="anchor"&gt;🔗&lt;/a&gt; &lt;a href="#top" aria-hidden="true" title="Back to top" class="anchor"&gt;↩&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We check this immediately after doing the macOS-style hidpi check, which skips
the rest of this if taken, i.e. if the window is on a Retina screen.&lt;/p&gt;
&lt;p&gt;Because of that, and because we call &lt;code&gt;SetProcessDPIAware&lt;/code&gt; on Windows at startup,
“reported DPI” has the following meanings (with examples from above scenarios
given):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;macOS: native, unscaled DPI. (255x255 on Retina, 137x137 on 4K)&lt;/li&gt;
&lt;li&gt;Windows: &lt;code&gt;96 * ui_scaling_factor&lt;/code&gt;. (144x144)&lt;/li&gt;
&lt;li&gt;Linux: native, unscaled DPI. (188x189)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So in any case, we see our DPI &amp;gt;= 100 and just run everything at 2x.  If the
Windows user happened to be running at 100%, then we would too, and would fit
right in.  If we REALLY cared about that happening on Linux too, we could go the
&lt;code&gt;xrdb -query&lt;/code&gt; path. That way lies misery, I know it.&lt;/p&gt;
&lt;/section&gt;
</content>
  </entry>
  <entry>
    <id>tag:lottia.net,2024-11-09:/notes/0013-git-jujutsu-miniature.html</id>
    <title type="html">Git and jujutsu: in miniature</title>
    <published>2024-11-09T03:30:00Z</published>
    <updated>2024-11-09T03:30:00Z</updated>
    <link rel="alternate" href="https://un5nvy3mxv5kcnr.julianrbryant.com/notes/0013-git-jujutsu-miniature.html" type="text/html"/>
    <content type="html">&lt;section id="top"&gt;
&lt;p&gt;Last night in bed, I realised we’d encountered a scenario at work during the day
where something happened so fluidly in jujutsu that it’d make a good case story!
Let’s compare, step by step, how it’d look with git.&lt;/p&gt;
&lt;p&gt;The stage is set: you’re working on a &lt;a href="https://un5wk9hruugzyq5uhkae4.julianrbryant.com"&gt;big, old, legacy codebase&lt;/a&gt;,
and you’re 10 commits deep in a branch where you’re adding a new parsing
component which will, by the time the branch is merge-ready, completely supplant
an old one and all its uses.&lt;/p&gt;
&lt;p&gt;The parser is mostly called through a centralised place, which is well-covered
by tests, so you can feel &lt;em&gt;reasonably&lt;/em&gt; assured that green CI will mean you’re
on the right track, and you’ve been removing parts of the old parser as you
introduce the new.&lt;/p&gt;
&lt;p&gt;But what’s this? There’s an outlying case where a method on some random model
calls into the component directly — unlike most uses, it’s just calling the
parser to clean up some input. And while the method is covered by a number of
tests, this particular function of it isn’t at all — you could replace the
parser with the identity function and these tests would be fine with that.&lt;/p&gt;
&lt;p&gt;So we need a new test. We’re currently in some other WIP on this branch, and the
original parser is half-taken to bits, so we’ll write the tests against trunk,
called &lt;code&gt;develop&lt;/code&gt; here.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-console?prompt=$"&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;jj new develop &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s1"&gt;'content_spec: assert body_excerpt strips tokens.'&lt;/span&gt;
&lt;span class="go"&gt;Working copy now at: rltuvkoz 9b9f6db4 (empty) content_spec: assert body_excerpt strips tokens.
Parent commit      : xkowykqr 83ad162d develop | (empty) Merge pull request #1736 from backfill-pr
Added 10 files, modified 46 files, removed 4 files&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class="language-console?prompt=$"&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;git checkout develop
&lt;span class="go"&gt;error: Your local changes to the following files would be overwritten by checkout:
        app/lib/rml_parser.rb
        spec/lib/rml_parser_spec.rb
Please commit your changes or stash them before you switch branches.
Aborting
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;git stash
&lt;span class="go"&gt;Saved working directory and index state WIP on (no branch): 478c7377 remove local artefacts.
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;git checkout develop
&lt;span class="go"&gt;Previous HEAD position was 478c7377 remove local artefacts.
Switched to branch 'develop'
Your branch is up to date with 'origin/develop'.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, a seasoned git developer (with a good vcs prompt for their shell) will know
to stash reflexively, and indeed the above three commands under my aliases would
be &lt;code&gt;co develop&lt;/code&gt;, &lt;code&gt;st&lt;/code&gt;, &lt;code&gt;co develop&lt;/code&gt;, but it’s interesting that we kind of have
to context-switch for a moment here and think, “OK, working copy changes, maybe
some tracked, some untracked, put them all into this ‘stash’ thing over there so
we can move around freely”.&lt;/p&gt;
&lt;p&gt;So we write our test, confirm it’s actually asserting the behaviour. We’re now here:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-console?prompt=$"&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;jj status
&lt;span class="go"&gt;Working copy changes:
M spec/models/content_spec.rb
Working copy : rltuvkoz e0186732 content_spec: assert body_excerpt strips tokens.
Parent commit: xkowykqr 83ad162d develop | (empty) Merge pull request #1736 from backfill-pr&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class="language-console?prompt=$"&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;git status &lt;span class="nt"&gt;-sb&lt;/span&gt;
&lt;span class="c"&gt;## develop...origin/develop
&lt;/span&gt;&lt;span class="go"&gt; M spec/models/content_spec.rb&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/section&gt;
&lt;section id="manoeuvre-jj"&gt;
&lt;h2&gt;The manoeuvre: jj &lt;a href="#manoeuvre-jj" aria-hidden="true" title="Permalink to section" class="anchor"&gt;🔗&lt;/a&gt; &lt;a href="#top" aria-hidden="true" title="Back to top" class="anchor"&gt;↩&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Great! We want to introduce this change into our branch, so we can be sure we
don’t break this use case. We don’t really want it right at the tip, since there’s a
progression of commits; we want it a few commits before.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-jjlog"&gt;&lt;span class="s bold"&gt;@&lt;/span&gt;  &lt;span class="m bold"&gt;rl&lt;/span&gt;&lt;span class="cm bold"&gt;tuvkoz&lt;/span&gt; &lt;span class="nn bold"&gt;ashe@kivikakk.ee&lt;/span&gt; &lt;span class="nt bold"&gt;2024-11-09 11:55:13&lt;/span&gt; &lt;span class="kp bold"&gt;e0&lt;/span&gt;&lt;span class="cm bold"&gt;186732&lt;/span&gt;
│  &lt;span class="s bold"&gt;content_spec: assert body_excerpt strips tokens.
&lt;/span&gt;&lt;span class="nt"&gt;◆&lt;/span&gt;    &lt;span class="m bold"&gt;xk&lt;/span&gt;&lt;span class="cm "&gt;owykqr&lt;/span&gt; &lt;span class="nn "&gt;redacted@redacted.com&lt;/span&gt; &lt;span class="nt "&gt;2024-11-08 15:30:54&lt;/span&gt; &lt;span class="m "&gt;develop&lt;/span&gt; &lt;span class="s"&gt;git_head()&lt;/span&gt; &lt;span class="kp bold"&gt;8&lt;/span&gt;&lt;span class="cm "&gt;3ad162d&lt;/span&gt;
├─╮  (empty) Merge pull request #1736 from backfill-pr
│ │
│ ~
│
&lt;span class="cm"&gt;~  (elided revisions)
&lt;/span&gt;│ ○  &lt;span class="m bold"&gt;xn&lt;/span&gt;&lt;span class="cm "&gt;srwqok&lt;/span&gt; &lt;span class="nn "&gt;ashe@kivikakk.ee&lt;/span&gt; &lt;span class="nt "&gt;2024-11-09 11:54:38&lt;/span&gt; &lt;span class="kp bold"&gt;ec&lt;/span&gt;&lt;span class="cm "&gt;3df6f8&lt;/span&gt;
│ │  (no description set)
│ ○  &lt;span class="m bold"&gt;rv&lt;/span&gt;&lt;span class="cm "&gt;umupsn&lt;/span&gt; &lt;span class="nn "&gt;ashe@kivikakk.ee&lt;/span&gt; &lt;span class="nt "&gt;2024-11-09 11:54:38&lt;/span&gt; &lt;span class="m "&gt;rml-parser*&lt;/span&gt; &lt;span class="kp bold"&gt;478&lt;/span&gt;&lt;span class="cm "&gt;c7377&lt;/span&gt;
│ │  remove local artefacts.
│ ○  &lt;span class="m bold"&gt;pp&lt;/span&gt;&lt;span class="cm "&gt;mltptr&lt;/span&gt; &lt;span class="nn "&gt;ashe@kivikakk.ee&lt;/span&gt; &lt;span class="nt "&gt;2024-11-09 11:54:38&lt;/span&gt; &lt;span class="kp bold"&gt;25b&lt;/span&gt;&lt;span class="cm "&gt;122ab&lt;/span&gt;
│ │  Token: migrate uses of #token to #original_token.
│ ○  &lt;span class="m bold"&gt;pyt&lt;/span&gt;&lt;span class="cm "&gt;wvkmx&lt;/span&gt; &lt;span class="nn "&gt;ashe@kivikakk.ee&lt;/span&gt; &lt;span class="nt "&gt;2024-11-09 11:54:38&lt;/span&gt; &lt;span class="kp bold"&gt;273&lt;/span&gt;&lt;span class="cm "&gt;16b9a&lt;/span&gt;
│ │  RmlParser: implementing in Token.
│ ○  &lt;span class="m bold"&gt;nl&lt;/span&gt;&lt;span class="cm "&gt;rsutxv&lt;/span&gt; &lt;span class="nn "&gt;ashe@kivikakk.ee&lt;/span&gt; &lt;span class="nt "&gt;2024-11-09 11:54:38&lt;/span&gt; &lt;span class="kp bold"&gt;3e7&lt;/span&gt;&lt;span class="cm "&gt;613e7&lt;/span&gt;
│ │  RmlParser: strip_tokens.
│ ○  &lt;span class="m bold"&gt;pyp&lt;/span&gt;&lt;span class="cm "&gt;uqnwp&lt;/span&gt; &lt;span class="nn "&gt;ashe@kivikakk.ee&lt;/span&gt; &lt;span class="nt "&gt;2024-11-08 16:08:40&lt;/span&gt; &lt;span class="kp bold"&gt;1be&lt;/span&gt;&lt;span class="cm "&gt;aebab&lt;/span&gt;
│ │  RmlParser: blocks everywhere, include offset in bc.
│ ○  &lt;span class="m bold"&gt;uv&lt;/span&gt;&lt;span class="cm "&gt;mwxovu&lt;/span&gt; &lt;span class="nn "&gt;ashe@kivikakk.ee&lt;/span&gt; &lt;span class="nt "&gt;2024-11-08 15:29:14&lt;/span&gt; &lt;span class="kp bold"&gt;b0c&lt;/span&gt;&lt;span class="cm "&gt;4bbba&lt;/span&gt;
│ │  RmlParser: test roundtrip.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Change &lt;code&gt;nlrsutxv&lt;/code&gt; (commit &lt;code&gt;3e7613e7&lt;/code&gt;) introduces the change we’d like the test
to inform, so we want to slot the test in right before then.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-console?prompt=$"&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;jj rebase &lt;span class="nt"&gt;-r&lt;/span&gt; @ &lt;span class="nt"&gt;-B&lt;/span&gt; &lt;span class="nb"&gt;nl&lt;/span&gt;
&lt;span class="go"&gt;Rebased 1 commits onto destination
Rebased 5 descendant commits
Working copy now at: rltuvkoz 99a0a2a0 content_spec: assert body_excerpt strips tokens.
Parent commit      : pypuqnwp 1beaebab RmlParser: blocks everywhere, include offset in bc.
Added 7 files, modified 39 files, removed 8 files&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href="https://un5ydn9hgyx6cem5tqpfy4k4ym.julianrbryant.com/jj/latest/cli-reference/#jj-rebase"&gt;&lt;code&gt;jj rebase&lt;/code&gt;&lt;/a&gt; can rebase a “branch” (&lt;code&gt;-b&lt;/code&gt;), a revision and its descendants
(&lt;code&gt;-s&lt;/code&gt;), or just a single revision (&lt;code&gt;-r&lt;/code&gt;). &lt;code&gt;-r @&lt;/code&gt; means the revision currently
edited in the working copy. &lt;code&gt;-B&lt;/code&gt; means “insert before”.&lt;/p&gt;
&lt;p&gt;The log now looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-jjlog"&gt;○  &lt;span class="m bold"&gt;xn&lt;/span&gt;&lt;span class="cm "&gt;srwqok&lt;/span&gt; &lt;span class="nn "&gt;ashe@kivikakk.ee&lt;/span&gt; &lt;span class="nt "&gt;2024-11-09 12:02:07&lt;/span&gt; &lt;span class="kp bold"&gt;cef&lt;/span&gt;&lt;span class="cm "&gt;52288&lt;/span&gt;
│  (no description set)
○  &lt;span class="m bold"&gt;rv&lt;/span&gt;&lt;span class="cm "&gt;umupsn&lt;/span&gt; &lt;span class="nn "&gt;ashe@kivikakk.ee&lt;/span&gt; &lt;span class="nt "&gt;2024-11-09 12:02:07&lt;/span&gt; &lt;span class="m "&gt;rml-parser*&lt;/span&gt; &lt;span class="kp bold"&gt;735&lt;/span&gt;&lt;span class="cm "&gt;abf79&lt;/span&gt;
│  remove local artefacts.
○  &lt;span class="m bold"&gt;pp&lt;/span&gt;&lt;span class="cm "&gt;mltptr&lt;/span&gt; &lt;span class="nn "&gt;ashe@kivikakk.ee&lt;/span&gt; &lt;span class="nt "&gt;2024-11-09 12:02:07&lt;/span&gt; &lt;span class="kp bold"&gt;f34&lt;/span&gt;&lt;span class="cm "&gt;335bf&lt;/span&gt;
│  Token: migrate uses of #token to #original_token.
○  &lt;span class="m bold"&gt;pyt&lt;/span&gt;&lt;span class="cm "&gt;wvkmx&lt;/span&gt; &lt;span class="nn "&gt;ashe@kivikakk.ee&lt;/span&gt; &lt;span class="nt "&gt;2024-11-09 12:02:07&lt;/span&gt; &lt;span class="kp bold"&gt;0ff9&lt;/span&gt;&lt;span class="cm "&gt;0a43&lt;/span&gt;
│  RmlParser: implementing in Token.
○  &lt;span class="m bold"&gt;nl&lt;/span&gt;&lt;span class="cm "&gt;rsutxv&lt;/span&gt; &lt;span class="nn "&gt;ashe@kivikakk.ee&lt;/span&gt; &lt;span class="nt "&gt;2024-11-09 12:02:07&lt;/span&gt; &lt;span class="kp bold"&gt;783&lt;/span&gt;&lt;span class="cm "&gt;90b57&lt;/span&gt;
│  RmlParser: strip_tokens.
&lt;span class="s bold"&gt;@&lt;/span&gt;  &lt;span class="m bold"&gt;rl&lt;/span&gt;&lt;span class="cm bold"&gt;tuvkoz&lt;/span&gt; &lt;span class="nn bold"&gt;ashe@kivikakk.ee&lt;/span&gt; &lt;span class="nt bold"&gt;2024-11-09 12:02:07&lt;/span&gt; &lt;span class="kp bold"&gt;99a&lt;/span&gt;&lt;span class="cm bold"&gt;0a2a0&lt;/span&gt;
│  &lt;span class="s bold"&gt;content_spec: assert body_excerpt strips tokens.
&lt;/span&gt;○  &lt;span class="m bold"&gt;pyp&lt;/span&gt;&lt;span class="cm "&gt;uqnwp&lt;/span&gt; &lt;span class="nn "&gt;ashe@kivikakk.ee&lt;/span&gt; &lt;span class="nt "&gt;2024-11-08 16:08:40&lt;/span&gt; &lt;span class="s"&gt;git_head()&lt;/span&gt; &lt;span class="kp bold"&gt;1be&lt;/span&gt;&lt;span class="cm "&gt;aebab&lt;/span&gt;
│  RmlParser: blocks everywhere, include offset in bc.
○  &lt;span class="m bold"&gt;uv&lt;/span&gt;&lt;span class="cm "&gt;mwxovu&lt;/span&gt; &lt;span class="nn "&gt;ashe@kivikakk.ee&lt;/span&gt; &lt;span class="nt "&gt;2024-11-08 15:29:14&lt;/span&gt; &lt;span class="kp bold"&gt;b0c&lt;/span&gt;&lt;span class="cm "&gt;4bbba&lt;/span&gt;
│  RmlParser: test roundtrip.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can now return to what we were doing, WIP ready for us to resume as we ever
were:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-console?prompt=$"&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;jj edit xn
&lt;span class="go"&gt;Working copy now at: xnsrwqok cef52288 (no description set)
Parent commit      : rvumupsn 735abf79 rml-parser* | remove local artefacts.
Added 1 files, modified 9 files, removed 6 files&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that git commit IDs have changed, as you’d expect, but the jj change
IDs haven’t. This stability of identity is very handy — &lt;code&gt;xn&lt;/code&gt; was what I was
working on before I started this aside, and it still is afterwards.&lt;/p&gt;
&lt;p&gt;The other side of this is the &lt;code&gt;rml-parser&lt;/code&gt; bookmark — jj’s equivalent to git’s
branches, but used far less frequently (most often for git interop) — has
&lt;em&gt;followed&lt;/em&gt; its change, with the asterisk after noting it’s diverged from the
remote one. You don’t have to chase down your branches after a rebase.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="manoeuvre-git"&gt;
&lt;h2&gt;The manoeuvre: git &lt;a href="#manoeuvre-git" aria-hidden="true" title="Permalink to section" class="anchor"&gt;🔗&lt;/a&gt; &lt;a href="#top" aria-hidden="true" title="Back to top" class="anchor"&gt;↩&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;How does the same play out with git? Let’s look at the commit log from git’s
point of view:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;* 478c7377a4 (Fri, 8 Nov 2024) - (rml-parser) remove local artefacts. &amp;lt;Asherah Connor&amp;gt;
* 25b122abd8 (Fri, 8 Nov 2024) - Token: migrate uses of #token to #original_token. &amp;lt;Asherah Connor&amp;gt;
* 27316b9a2e (Fri, 8 Nov 2024) - RmlParser: implementing in Token. &amp;lt;Asherah Connor&amp;gt;
* 3e7613e704 (Fri, 8 Nov 2024) - RmlParser: strip_tokens. &amp;lt;Asherah Connor&amp;gt;
* 1beaebab8c (Fri, 8 Nov 2024) - RmlParser: blocks everywhere, include offset in bc. &amp;lt;Asherah Connor&amp;gt;
* b0c4bbbafb (Fri, 8 Nov 2024) - RmlParser: test roundtrip. &amp;lt;Asherah Connor&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We want to introduce our change — currently just an untracked change in the
working tree, with &lt;code&gt;develop&lt;/code&gt; checked out — before commit &lt;code&gt;3e7613e704&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We have two ways of getting it there:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Stash the change, interactively rebase &lt;code&gt;rml-parser&lt;/code&gt; and stop after the
previous commit (&lt;code&gt;edit 1beaebab8c&lt;/code&gt;), pop stash, commit, continue rebase.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Commit the change now, interactively rebase &lt;code&gt;rml-parser&lt;/code&gt; and add a &lt;code&gt;pick&lt;/code&gt;
line for our new commit in the right place.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I tend to commit early and often, so I’m more a fan of #2 as a rule, and
we’re also already managing one item on the stash (our WIP from the tip of the
branch), and while there’s no problem with putting as much as we want on it
(it’s a very competent stack!), I just don’t wanna.&lt;/p&gt;
&lt;p&gt;Alas, more choices:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Commit to &lt;code&gt;develop&lt;/code&gt;, copy the commit ID to the pasteboard, hard reset
&lt;code&gt;develop&lt;/code&gt; back to its previous value.&lt;/li&gt;
&lt;li&gt;Create a new branch, commit to that, delete branch when done.&lt;/li&gt;
&lt;li&gt;Detach HEAD, calm your git’s nerves, it’s ok I promise&lt;sup class="footnote-ref"&gt;&lt;a href="#fn-advice" id="fnref-advice" data-footnote-ref=""&gt;1&lt;/a&gt;&lt;/sup&gt;, commit.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We don’t really want &lt;em&gt;this&lt;/em&gt; commit on a branch, but git really wants us to want
a branch. Let’s go with purity.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-console?prompt=$"&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;git checkout &lt;span class="nt"&gt;--detach&lt;/span&gt;
&lt;span class="go"&gt;M       spec/models/content_spec.rb
HEAD is now at 83ad162d7c Merge pull request #1736 from backfill-pr
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;git add &lt;span class="nt"&gt;-p&lt;/span&gt;
&lt;span class="go"&gt;[...]
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s1"&gt;'content_spec: assert body_excerpt strips tokens.'&lt;/span&gt;
&lt;span class="go"&gt;[detached HEAD 6005753744] content_spec: assert body_excerpt strips tokens.
 1 file changed, 8 insertions(+)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now it’s time for the manoeuvre.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-console?prompt=$"&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;git checkout rml-parser
&lt;span class="go"&gt;Warning: you are leaving 1 commit behind, not connected to
any of your branches:

  6005753744 content_spec: assert body_excerpt strips tokens.

Switched to branch 'rml-parser'
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;git rebase &lt;span class="nt"&gt;-i&lt;/span&gt; 3e7613^&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We’re presented with this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pick 3e7613e704 RmlParser: strip_tokens.
pick 27316b9a2e RmlParser: implementing in Token.
pick 25b122abd8 Token: migrate uses of #token to #original_token.
pick 478c7377a4 remove local artefacts.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Very easy: we add &lt;code&gt;pick 6005753744&lt;/code&gt; above the first line, save and quit.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-console?prompt=$"&gt;&lt;span class="go"&gt;Successfully rebased and updated refs/heads/rml-parser.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here’s our git log:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;* 7b836073ca (Fri, 8 Nov 2024) - (HEAD -&amp;gt; rml-parser) remove local artefacts. &amp;lt;Asherah Connor&amp;gt;
* 70626ff5cb (Fri, 8 Nov 2024) - Token: migrate uses of #token to #original_token. &amp;lt;Asherah Connor&amp;gt;
* b4089df702 (Fri, 8 Nov 2024) - RmlParser: implementing in Token. &amp;lt;Asherah Connor&amp;gt;
* e385f17253 (Fri, 8 Nov 2024) - RmlParser: strip_tokens. &amp;lt;Asherah Connor&amp;gt;
* 652607c2f0 (Sat, 9 Nov 2024) - content_spec: assert body_excerpt strips tokens. &amp;lt;Asherah Connor&amp;gt;
* 1beaebab8c (Fri, 8 Nov 2024) - RmlParser: blocks everywhere, include offset in bc. &amp;lt;Asherah Connor&amp;gt;
* b0c4bbbafb (Fri, 8 Nov 2024) - RmlParser: test roundtrip. &amp;lt;Asherah Connor&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Don’t forget to pop the stash!&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-console?prompt=$"&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;git stash pop
&lt;span class="go"&gt;On branch rml-parser
Changes to be committed:
  (use "git restore --staged &amp;lt;file&amp;gt;..." to unstage)
        new file:   app/lib/rml_parser_flux.rb

Changes not staged for commit:
  (use "git add &amp;lt;file&amp;gt;..." to update what will be committed)
  (use "git restore &amp;lt;file&amp;gt;..." to discard changes in working directory)
        modified:   app/lib/rml_parser.rb
        modified:   spec/lib/rml_parser_spec.rb

Dropped refs/stash@{0} (d5c281196fa218a33a55538111fa7770284ca2cb)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Damn. I forgot to supply &lt;code&gt;--index&lt;/code&gt;, and only new files (which were tracked
at the time of &lt;code&gt;stash&lt;/code&gt;) are added to the index; all other stashed changes are
restored into the working copy, but not into the index. Oh well, it’s git: I can
just go again with the stash reference from the last line.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="remarks"&gt;
&lt;h2&gt;Remarks &lt;a href="#remarks" aria-hidden="true" title="Permalink to section" class="anchor"&gt;🔗&lt;/a&gt; &lt;a href="#top" aria-hidden="true" title="Back to top" class="anchor"&gt;↩&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The bit that got me was that git really forces me to make a lot of decisions I
don’t actually care about. How will I save my WIP while I’m off on this quest?
Do I want to juggle stashes and my working tree, or throw commits around? How
will I get a commit where I want it? Do I need to come up with a branch name?
And how much of all this needs to just sit in my head or pasteboard, lest I
forget what I was in the middle of?&lt;/p&gt;
&lt;p&gt;It’s astonishing, too, that one of the most powerful tools git has to rewrite
history is “provide a script which sequentially constructs the DAG you want,
where you can insert breakpoints to manually do things you can’t express in the
script”. With jj you just .. put the commit there. It was already in a commit
because everything is. And when you go back to where you were, everything is
still there, because it was a commit, too, just an unfinished one.&lt;/p&gt;
&lt;/section&gt;
&lt;section class="footnotes" data-footnotes="" id="footnotes"&gt;&lt;h2&gt;Footnotes &lt;a href="#footnotes" aria-hidden="true" title="Permalink to section" class="anchor"&gt;🔗&lt;/a&gt; &lt;a href="#top" aria-hidden="true" title="Back to top" class="anchor"&gt;↩&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li id="fn-advice"&gt;
&lt;p&gt;&lt;code&gt;git config --global advice.detachedHead false&lt;/code&gt; &lt;a href="#fnref-advice" class="footnote-backref" data-footnote-backref="" data-footnote-backref-idx="1" aria-label="Back to reference 1"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content>
  </entry>
  <entry>
    <id>tag:lottia.net,2024-11-07:/notes/0012-soft-skills.html</id>
    <title type="html">Soft skills: jujutsu early feelings</title>
    <published>2024-11-07T07:07:00Z</published>
    <updated>2024-11-07T07:07:00Z</updated>
    <link rel="alternate" href="https://un5nvy3mxv5kcnr.julianrbryant.com/notes/0012-soft-skills.html" type="text/html"/>
    <content type="html">&lt;section id="top"&gt;
&lt;p&gt;I finally got bothered to try &lt;a href="https://github.com/martinvonz/jj"&gt;jujutsu&lt;/a&gt;. It’s often hard to convey just how fast
I’m accustomed to moving with git, and so while there are many valid complaints
about its interface, object model design, etc., I’m really super fluent with it!&lt;/p&gt;
&lt;p&gt;So for a long time I was happy to let jujutsu just be a thing people were
talking about; simpler or better than git and so on, I didn’t ever quite hear
anything that made learning it sound worthwhile yet.&lt;/p&gt;
&lt;p&gt;Recently &lt;a href="https://lobste.rs/s/mdfhda/steve_klabnik_s_tutorial_on_jujutsu_git"&gt;Steve Klabnik’s Jujutsu Tutorial passed by on Lobste.rs&lt;/a&gt;
again, and it was enough to get &lt;a href="https://uhm6mk44w9dxcm21w2av49q1.julianrbryant.com"&gt;Ashe&lt;/a&gt; interested. After a few days of them having
a look, I bit the bullet and insisted we set up for both of us to use it&lt;sup class="footnote-ref"&gt;&lt;a href="#fn-dual" id="fnref-dual" data-footnote-ref=""&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;It’s really good. We’ve been using it on every git repo we touch, to maximise
our exposure and really put it through its paces, and it’s been so unexpectedly
rewarding.&lt;/p&gt;
&lt;p&gt;Fluent git can involve a lot of rewriting history and working on and with the
commit DAG itself. jj elevates many of the involved operations to first-class
status, and jj’s answer to git’s index — that the working tree represents the
state of the currently-edited commit, and editing the working tree edits the
commit — is the core of the mind-frame shift, like kak/hx’s pivot re: vi about
command sentence order.&lt;/p&gt;
&lt;p&gt;Things that combine to change how things work include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The working tree reifies the edited commit, which obviates the index. This
simplifies the design and allows for so many more fully-general operations.
&lt;ul&gt;
&lt;li&gt;Practically speaking, jj snapshots the working tree at the start of every
command. (Now you know.)&lt;/li&gt;
&lt;li&gt;As above, imo this is the core mind-frame shift required, and is what I see
the most wailing and gnashing of teeth about. Do it.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Rebases &lt;em&gt;always&lt;/em&gt; succeed, with conflicts stored meaningfully in history.
Conflict resolution propagates automatically to descendent &lt;em&gt;changes&lt;/em&gt;, which
are not commits.
&lt;ul&gt;
&lt;li&gt;It’s a little hard to describe exactly how neat this actually can be and is
without your head already being in the jj model. If it sounds a bit magic,
compared to the status quo, it honestly is.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;git’s promise of at-least-never-losing-any-data is elevated to the level of
the repository itself in the operation log.
&lt;ul&gt;
&lt;li&gt;Sometimes knee-deep in a complicated interactive rebase, I’ll realise I’ve
made a few too many wrong moves and it’s easier just to abort and start
over. The data’s always safe, but on occasion you’ll need to visit the
reflog to retrieve it, and manoeuvring the repo back into a particular
gnarly conflicted merge state can be frustrating. jj characterises every
state-modifying operation in the “operation log”, so you can instead play
back and forth whatever you just did &lt;em&gt;to&lt;/em&gt; your repo, and not just what’s
&lt;em&gt;in&lt;/em&gt; it.&lt;/li&gt;
&lt;li&gt;“Snapshot the working tree” is an example of an operation recorded in the
oplog; it’s not magic.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;If cheaper branching was a selling point for git&lt;sup class="footnote-ref"&gt;&lt;a href="#fn-svn" id="fnref-svn" data-footnote-ref=""&gt;2&lt;/a&gt;&lt;/sup&gt;, then branchless is the
epitome of that selling point.
&lt;ul&gt;
&lt;li&gt;Every change can be a branch. Typical git usage relies on branches to know
which commits in the database should be considered “reachable” and therefore
not irrelevant and to be garbage collected. jj puts the heuristic in a
different place: instead of guessing which commits are relevant, record when
they &lt;em&gt;become&lt;/em&gt; irrelevant. Moving off an empty commit? Abandon it. Edited
a commit, or rebased some? Abandon the old version/s. When you make a few
changes on a few new commits, they all simply stay put, and you can
incorporate/ rebase/abandon them later as you see fit. That’s how you
accidentally an entire “stash” concept, and it’s &lt;em&gt;easier&lt;/em&gt; to use than git
stash.&lt;/li&gt;
&lt;li&gt;Note the implication: you might accidentally some other (perhaps new!)
concepts too, just from your regular jj use evolving some patterns.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Made this post because I noticed my log currently looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-jjlog"&gt;&lt;span class="s bold"&gt;@&lt;/span&gt;  &lt;span class="m bold"&gt;n&lt;/span&gt;&lt;span class="cm bold"&gt;vxttvku&lt;/span&gt; &lt;span class="nn bold"&gt;charlotte@lottia.net&lt;/span&gt; &lt;span class="nt bold"&gt;2024-11-05 13:19:23&lt;/span&gt; &lt;span class="kp bold"&gt;8&lt;/span&gt;&lt;span class="cm bold"&gt;dbb3ac9&lt;/span&gt;
│  &lt;span class="s bold"&gt;(empty) (no description set)
&lt;/span&gt;○  &lt;span class="m bold"&gt;o&lt;/span&gt;&lt;span class="cm "&gt;ypwlzvr&lt;/span&gt; &lt;span class="nn "&gt;charlotte@lottia.net&lt;/span&gt; &lt;span class="nt "&gt;2024-11-05 13:18:23&lt;/span&gt; &lt;span class="s"&gt;git_head()&lt;/span&gt; &lt;span class="kp bold"&gt;9&lt;/span&gt;&lt;span class="cm "&gt;d6049b2&lt;/span&gt;
│  Imtui: object system, sigh
│ ○  &lt;span class="m bold"&gt;rq&lt;/span&gt;&lt;span class="cm "&gt;kpqpsk&lt;/span&gt; &lt;span class="nn "&gt;charlotte@lottia.net&lt;/span&gt; &lt;span class="nt "&gt;2024-11-05 13:19:07&lt;/span&gt; &lt;span class="kp bold"&gt;0&lt;/span&gt;&lt;span class="cm "&gt;8d3afb3&lt;/span&gt;
├─╯  play around with https://un5t0br5cfrx6zm5.julianrbryant.com/devlog/2024/#2024-11-04
&lt;span class="nt"&gt;◆&lt;/span&gt;  &lt;span class="m bold"&gt;y&lt;/span&gt;&lt;span class="cm "&gt;mxnqszq&lt;/span&gt; &lt;span class="nn "&gt;charlotte@lottia.net&lt;/span&gt; &lt;span class="nt "&gt;2024-10-29 17:08:48&lt;/span&gt; &lt;span class="m "&gt;main&lt;/span&gt; &lt;span class="kp bold"&gt;fa&lt;/span&gt;&lt;span class="cm "&gt;7f5da9&lt;/span&gt;
│  Imtui: experiment with "generations" to GC/disregard killed components
&lt;span class="cm"&gt;~  (elided revisions)
&lt;/span&gt;│ ○  &lt;span class="m bold"&gt;rs&lt;/span&gt;&lt;span class="cm "&gt;xxxkzu&lt;/span&gt; &lt;span class="nn "&gt;charlotte@lottia.net&lt;/span&gt; &lt;span class="nt "&gt;2024-10-28 11:19:02&lt;/span&gt; &lt;span class="kp bold"&gt;2&lt;/span&gt;&lt;span class="cm "&gt;819f0d1&lt;/span&gt;
├─╯  Imtui: editors cont'd
&lt;span class="nt"&gt;◆&lt;/span&gt;  &lt;span class="m bold"&gt;w&lt;/span&gt;&lt;span class="cm "&gt;vwyouqo&lt;/span&gt; &lt;span class="nn "&gt;charlotte@lottia.net&lt;/span&gt; &lt;span class="nt "&gt;2024-10-28 11:13:26&lt;/span&gt; &lt;span class="kp bold"&gt;f4&lt;/span&gt;&lt;span class="cm "&gt;a61c28&lt;/span&gt;
│  Imtui: editors init&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Multiple branches, without names! It’s happening.&lt;/p&gt;
&lt;/section&gt;
&lt;section class="footnotes" data-footnotes="" id="footnotes"&gt;&lt;h2&gt;Footnotes &lt;a href="#footnotes" aria-hidden="true" title="Permalink to section" class="anchor"&gt;🔗&lt;/a&gt; &lt;a href="#top" aria-hidden="true" title="Back to top" class="anchor"&gt;↩&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li id="fn-dual"&gt;
&lt;p&gt;tl;dr: &lt;a href="https://un5zt09ugh70.julianrbryant.com/~talya/vyx/blob/main/fadc9f4d18e2cb33cb283cc459c39309e2adac36/home/fish.nix#L29-L54"&gt;this&lt;/a&gt;, where &lt;code&gt;A&lt;/code&gt; and &lt;code&gt;C&lt;/code&gt; run &lt;code&gt;jj&lt;/code&gt; with &lt;code&gt;JJ_USER&lt;/code&gt; and
&lt;code&gt;JJ_EMAIL&lt;/code&gt; set accordingly. &lt;a href="#fnref-dual" class="footnote-backref" data-footnote-backref="" data-footnote-backref-idx="1" aria-label="Back to reference 1"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn-svn"&gt;
&lt;p&gt;At the time git started to really gain popularity, &lt;a href="https://un5m20b41nru4enuvr8wj9h0br.julianrbryant.com/"&gt;Subversion&lt;/a&gt; was popular,
which itself became popular in part because it made branches so cheap. Subversion
implemented branches as simply copies of a directory — typically the root
would have a &lt;code&gt;trunk&lt;/code&gt; directory, equivalent to today’s &lt;code&gt;main&lt;/code&gt;, and to make a
branch, you just recursively copy that to a subdirectory of the &lt;code&gt;branches&lt;/code&gt; root
directory. Subversion’s key improvement is that the tree isn’t actually
copied in the repository; it can reuse the existing one, with deltas applied
separately. Keep in mind the much smaller harddrives and lower connection
speeds of the time.
&lt;br&gt;
This led to very busy trees, however, and commits to different branches would
still interfere with each other and the mainline since they’re in actual fact
all sequential commits to one big tree, not to mention the much poorer tools for
dealing with branches-as-embodied-in-the-tree. When the contents of a repo
are numerous or large enough, this quickly becomes an end-user cost as well:
the branches may be links on the server, but on your local filesystem they’re
full copies.
&lt;br&gt;
The costs are all doubled, too; Subversion’s primary local knowledge of the
repository (remember that it doesn’t store history/isn’t a “clone”!) is
kept in the form of a copy of every currently checked-out file, used as the
basis for a quick status/diff to know what you have changed. So 10MB of files
stored in trunk and one branch will use 40MB of space on your disk.
&lt;br&gt;
Branching in Subversion requires online access to the repository, permissions
to create the target, local diskspace to accommodate two copies of the
source, and a repository-unique name.
&lt;br&gt;
git’s by comparison needs none of the first three, and the last only
locally. &lt;a href="#fnref-svn" class="footnote-backref" data-footnote-backref="" data-footnote-backref-idx="2" aria-label="Back to reference 2"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content>
  </entry>
  <entry>
    <id>tag:lottia.net,2024-10-29:/notes/0011-non-intrusive-vtable.html</id>
    <title type="html">Non-intrusive vtable</title>
    <published>2024-10-29T07:07:00Z</published>
    <updated>2024-10-29T07:07:00Z</updated>
    <link rel="alternate" href="https://un5nvy3mxv5kcnr.julianrbryant.com/notes/0011-non-intrusive-vtable.html" type="text/html"/>
    <content type="html">&lt;section id="top"&gt;
&lt;pre&gt;&lt;code class="language-zig"&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Control&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;union&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;enum&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Controls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;menubar&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Controls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;Menubar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;menu&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Controls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;Menu&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;menu_item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Controls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;MenuItem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;editor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Controls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;Editor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;generation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Control&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;usize&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;inline&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;generation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;setGeneration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Control&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;inline&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;generation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;deinit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Control&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;inline&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deinit&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I’m calling this thing a “non-intrusive vtable”. A brief description for the
less familiar with Zig.&lt;/p&gt;
&lt;p&gt;In short, we have a tagged union, &lt;code&gt;Control&lt;/code&gt;, that can store a pointer to one
of the &lt;code&gt;Controls&lt;/code&gt; types listed. Because it’s a &lt;a href="https://un5t0br5cfrx6zm5.julianrbryant.com/documentation/master/#Tagged-union"&gt;tagged union&lt;/a&gt;, the variable
itself stores both the pointer as well as a “tag” value indicating which of the
variants is being stored, so there’s no type confusion.&lt;/p&gt;
&lt;p&gt;(One of these is unfortunately still 16 bytes on 64-bit systems: the tag,
which could theoretically be stored in 3 bits, nonetheless has a full 64 bits
allocated to it, because the payload that follows is a pointer, which must be
aligned. ¯\_(ツ)_/¯)&lt;/p&gt;
&lt;p&gt;In regular Zig code you can &lt;code&gt;switch&lt;/code&gt; on the value to get at the payload:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-zig"&gt;&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Controls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;Button&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c"&gt;// pretend we have one&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Control&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;button&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;button&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c"&gt;// this branch will run, with p == b.&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;menubar&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="n"&gt;mb&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c"&gt;// won't run in this case, but you get the idea.&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c"&gt;// mandatory default if all cases aren't handled explicitly!&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, these &lt;code&gt;Controls&lt;/code&gt; types all carry a &lt;code&gt;generation: usize&lt;/code&gt; member. One way of
getting the generation of an arbitrary &lt;code&gt;Control&lt;/code&gt; would be this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-zig"&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;getControlGeneration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Control&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;usize&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;button&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;generation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;menubar&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="n"&gt;mb&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;mb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;generation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;menu&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;generation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;menu_item&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="n"&gt;mi&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;mi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;generation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;editor&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;generation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This works fine, and is probably optimal. But what’s even more optimal is letting
comptime do the codegen for you. Returning to the definition above, we find:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-zig"&gt;    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;generation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Control&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;usize&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;inline&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;generation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href="https://un5t0br5cfrx6zm5.julianrbryant.com/documentation/master/#Inline-Switch-Prongs"&gt;&lt;code&gt;inline else&lt;/code&gt;&lt;/a&gt; is &lt;code&gt;inline&lt;/code&gt; in the same way &lt;a href="https://un5t0br5cfrx6zm5.julianrbryant.com/documentation/master/#inline-for"&gt;&lt;code&gt;inline for&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://un5t0br5cfrx6zm5.julianrbryant.com/documentation/master/#inline-while"&gt;&lt;code&gt;inline while&lt;/code&gt;&lt;/a&gt;
are. For every unhandled case, a prong is unrolled. This means the body must
compile for each possible capture (i.e. each payload type). (You can of course
do comptime calls here, to do different things with different kinds of payloads,
though please consider your complexity budget!)&lt;/p&gt;
&lt;p&gt;In this way, we create dispatch functions that encode the knowledge of (biggest air
quotes in the world) “all their subtypes’ implementations”. Ha ha ha.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="aside-on-tags"&gt;
&lt;h3&gt;Aside on tags &lt;a href="#aside-on-tags" aria-hidden="true" title="Permalink to section" class="anchor"&gt;🔗&lt;/a&gt; &lt;a href="#top" aria-hidden="true" title="Back to top" class="anchor"&gt;↩&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;As a kind-of-side, another neat thing Zig affords when working with tagged
unions is call-site ergonomics when passing them to and from functions.&lt;/p&gt;
&lt;p&gt;We have &lt;code&gt;Control&lt;/code&gt;s stored in a hashmap with a string lookup. Here’s a first
version of a “get control by ID” function:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-zig"&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;controlById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="n"&gt;Control&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;controls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Too easy. But now using it looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-zig"&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;controlById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"menubar"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;mb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;menubar&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;// &amp;lt;-- safety-checked variant assertion&lt;/span&gt;
        &lt;span class="c"&gt;// mb has type *Controls.Menubar.&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And indeed, most actual uses will probably need an intermediate, since most
call-sites will actually know the type of what they’re asking for. Why not
instead roll that into the getter?&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-zig"&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;controlById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;comptime&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Control&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TagPayload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Control&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;controls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;orelse&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;@field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;@tagName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href="https://un5t0br5cfrx6zm5.julianrbryant.com/documentation/master/std/#std.meta.Tag"&gt;&lt;code&gt;std.meta.Tag(Control)&lt;/code&gt;&lt;/a&gt; gets the type of the “tag” type of the
given type. What a mouthful. In other words, &lt;code&gt;Control&lt;/code&gt; is a tagged union, and
the tag is the implicitly-defined enum that represents which variant is chosen.
For &lt;code&gt;Control&lt;/code&gt;, that enum takes the values &lt;code&gt;.button&lt;/code&gt;, &lt;code&gt;.menubar&lt;/code&gt;, &lt;code&gt;.menu&lt;/code&gt;, etc.&lt;/p&gt;
&lt;p&gt;We take the expected tag of &lt;code&gt;Control&lt;/code&gt; at comptime, and declare our return type
to be (the optional of) the payload in &lt;code&gt;Control&lt;/code&gt; that corresponds to &lt;code&gt;tag&lt;/code&gt;,
using &lt;a href="https://un5t0br5cfrx6zm5.julianrbryant.com/documentation/master/std/#std.meta.TagPayload"&gt;&lt;code&gt;std.meta.TagPayload(Control, tag)&lt;/code&gt;&lt;/a&gt;. So a call
like &lt;code&gt;controlById(.button, "xyz")&lt;/code&gt; has the return type &lt;code&gt;?*Controls.Button&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;(Zig will unroll one of these functions per distinct &lt;code&gt;tag&lt;/code&gt;, and so type-errors
may occur if something doesn’t match up for one particular case! Conversely,
you can write code that wouldn’t check for all variants if you don’t plan on
using it for those, but it’s a friendly act to yourself and others to write
explicit checks with explicit messages. :)&lt;/p&gt;
&lt;p&gt;We fetch the &lt;code&gt;Control&lt;/code&gt; as before, but now we use the &lt;a href="https://un5t0br5cfrx6zm5.julianrbryant.com/documentation/master/#field"&gt;&lt;code&gt;@field&lt;/code&gt;&lt;/a&gt; builtin to
perform the equivalent of &lt;code&gt;c.blah&lt;/code&gt;, where &lt;code&gt;blah&lt;/code&gt; is specified by
&lt;a href="https://un5t0br5cfrx6zm5.julianrbryant.com/documentation/master/#tagName"&gt;&lt;code&gt;@tagName(tag)&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://un5t0br5cfrx6zm5.julianrbryant.com/documentation/master/#tagName"&gt;&lt;code&gt;@tagName&lt;/code&gt;&lt;/a&gt; builtin returns a comptime string of the tag’s name, so for
&lt;code&gt;.button&lt;/code&gt; it gives &lt;code&gt;"button"&lt;/code&gt;. This is what &lt;a href="https://un5t0br5cfrx6zm5.julianrbryant.com/documentation/master/#field"&gt;&lt;code&gt;@field&lt;/code&gt;&lt;/a&gt; wants, and so the effect
is a safety-checked variant assertion, like we were doing in our “user” code
before, only now it’s done as part of the getter itself.&lt;/p&gt;
&lt;p&gt;If you wanted to, you could even make the function non-asserting, instead
returning &lt;code&gt;null&lt;/code&gt; if the type was a mismatch:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-zig"&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;controlById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;comptime&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Control&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TagPayload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Control&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;controls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;orelse&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;tag&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You get the idea! You can do loads with this stuff.&lt;/p&gt;
&lt;/section&gt;
</content>
  </entry>
  <entry>
    <id>tag:lottia.net,2024-07-19:/notes/0010-vyxos.html</id>
    <title type="html">VyxOS</title>
    <published>2024-07-19T11:27:00Z</published>
    <updated>2024-07-19T11:27:00Z</updated>
    <link rel="alternate" href="https://un5nvy3mxv5kcnr.julianrbryant.com/notes/0010-vyxos.html" type="text/html"/>
    <content type="html">&lt;section id="top"&gt;
&lt;p&gt;Just a little note to say that I’ve published &lt;a href="https://un5zt09ugh70.julianrbryant.com/~talya/vyx"&gt;VyxOS&lt;/a&gt;, my Nix configuration.&lt;/p&gt;
&lt;p&gt;It’s just over a year old at this point, and at one point supported a large mesh
of eccentric servers with its own dynamic mesh and DNS and all sorts of
nonsense, but the config and infra has been hugely condensed to make it friendly
to my brain, and the history scrubbed to make it friendly to publishing.&lt;/p&gt;
&lt;p&gt;Hope it’s interesting!&lt;/p&gt;
&lt;/section&gt;
</content>
  </entry>
  <entry>
    <id>tag:lottia.net,2024-07-06:/notes/0009-time-travel-raw.html</id>
    <title type="html">Time travel, raw</title>
    <published>2024-07-06T16:21:00Z</published>
    <updated>2024-07-06T16:21:00Z</updated>
    <link rel="alternate" href="https://un5nvy3mxv5kcnr.julianrbryant.com/notes/0009-time-travel-raw.html" type="text/html"/>
    <content type="html">&lt;section id="top"&gt;
&lt;p&gt;Raw log from my notes re: &lt;a href="0008-time-travel.html"&gt;Time travel&lt;/a&gt; follows.&lt;/p&gt;
&lt;h2&gt;Sae&lt;/h2&gt;
&lt;p&gt;RV32I with some RV32C/refactoring WIP from long ago. The WIP probably feels way too magic for me now, but we should take a look at it. Now uses Niar.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="todos"&gt;
&lt;h3&gt;TODOs &lt;a href="#todos" aria-hidden="true" title="Permalink to section" class="anchor"&gt;🔗&lt;/a&gt; &lt;a href="#top" aria-hidden="true" title="Back to top" class="anchor"&gt;↩&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#decombing"&gt;Decombing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="#rv32c"&gt;RV32C&lt;/a&gt; and associated refactor
&lt;ul&gt;
&lt;li&gt;Then add RV32E, RV64I?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The entire test infra could be so much more robust.&lt;/li&gt;
&lt;li&gt;M extension&lt;/li&gt;
&lt;li&gt;A extension&lt;/li&gt;
&lt;li&gt;“Zicsr” extension&lt;/li&gt;
&lt;li&gt;BMC (WHAT DID THIS MEAN)&lt;/li&gt;
&lt;li&gt;ili9341spi interface&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id="decombing"&gt;
&lt;h3&gt;Decombing &lt;a href="#decombing" aria-hidden="true" title="Permalink to section" class="anchor"&gt;🔗&lt;/a&gt; &lt;a href="#top" aria-hidden="true" title="Back to top" class="anchor"&gt;↩&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;First priority is decombing the design to try to get the build time down. It’s currently redonkulous:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[2024-07-03 13:05:24,917] niar: INFO: building sae for icebreaker
[2024-07-03 13:05:24,917] niar: DEBUG: starting elaboration
[2024-07-03 13:05:25,148] niar: DEBUG: elaboration finished in 0:00:00.230441
[2024-07-03 13:05:25,148] niar: DEBUG: 'sae.il': 425,987 bytes
[2024-07-03 13:05:25,148] niar: DEBUG: starting synthesis/pnr
[2024-07-03 13:05:25,148] niar: INFO: [run]   execute_build
[2024-07-03 13:08:12,179] niar: DEBUG: synthesis/pnr finished in 0:02:47.031564
[2024-07-03 13:08:12,207] niar: INFO: 
[2024-07-03 13:08:12,207] niar: INFO: === sae ===
[2024-07-03 13:08:12,207] niar: INFO: 
[2024-07-03 13:08:12,207] niar: INFO:    Number of wires:               2859
[2024-07-03 13:08:12,207] niar: INFO:    Number of wire bits:           9313
[2024-07-03 13:08:12,207] niar: INFO:    Number of public wires:        2859
[2024-07-03 13:08:12,208] niar: INFO:    Number of public wire bits:    9313
[2024-07-03 13:08:12,208] niar: INFO:    Number of ports:                  4
[2024-07-03 13:08:12,208] niar: INFO:    Number of port bits:              4
[2024-07-03 13:08:12,208] niar: INFO:    Number of memories:               0
[2024-07-03 13:08:12,208] niar: INFO:    Number of memory bits:            0
[2024-07-03 13:08:12,208] niar: INFO:    Number of processes:              0
[2024-07-03 13:08:12,208] niar: INFO:    Number of cells:               5732
[2024-07-03 13:08:12,208] niar: INFO:      $scopeinfo                     19
[2024-07-03 13:08:12,208] niar: INFO:      SB_CARRY                      452
[2024-07-03 13:08:12,208] niar: INFO:      SB_DFF                         79
[2024-07-03 13:08:12,208] niar: INFO:      SB_DFFE                        35
[2024-07-03 13:08:12,208] niar: INFO:      SB_DFFESR                    1380
[2024-07-03 13:08:12,208] niar: INFO:      SB_DFFSR                        8
[2024-07-03 13:08:12,208] niar: INFO:      SB_GB_IO                        1
[2024-07-03 13:08:12,208] niar: INFO:      SB_IO                           3
[2024-07-03 13:08:12,208] niar: INFO:      SB_LUT4                      3737
[2024-07-03 13:08:12,208] niar: INFO:      SB_RAM40_4K                    18
[2024-07-03 13:08:12,208] niar: INFO: 
[2024-07-03 13:08:12,208] niar: INFO: Device utilisation:
[2024-07-03 13:08:12,208] niar: INFO:            ICESTORM_LC:  5033/ 5280    95%
[2024-07-03 13:08:12,208] niar: INFO:           ICESTORM_RAM:    18/   30    60%
[2024-07-03 13:08:12,208] niar: INFO:                  SB_IO:     4/   96     4%
[2024-07-03 13:08:12,208] niar: INFO:                  SB_GB:     5/    8    62%
[2024-07-03 13:08:12,208] niar: INFO:           ICESTORM_PLL:     0/    1     0%
[2024-07-03 13:08:12,208] niar: INFO:            SB_WARMBOOT:     0/    1     0%
[2024-07-03 13:08:12,208] niar: INFO:           ICESTORM_DSP:     0/    8     0%
[2024-07-03 13:08:12,208] niar: INFO:         ICESTORM_HFOSC:     0/    1     0%
[2024-07-03 13:08:12,208] niar: INFO:         ICESTORM_LFOSC:     0/    1     0%
[2024-07-03 13:08:12,208] niar: INFO:                 SB_I2C:     0/    2     0%
[2024-07-03 13:08:12,208] niar: INFO:                 SB_SPI:     0/    2     0%
[2024-07-03 13:08:12,208] niar: INFO:                 IO_I3C:     0/    2     0%
[2024-07-03 13:08:12,208] niar: INFO:            SB_LEDDA_IP:     0/    1     0%
[2024-07-03 13:08:12,208] niar: INFO:            SB_RGBA_DRV:     0/    1     0%
[2024-07-03 13:08:12,208] niar: INFO:         ICESTORM_SPRAM:     0/    4     0%
[2024-07-03 13:08:12,208] niar: INFO: 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After moving the fault check out of &lt;code&gt;fetch.resolve&lt;/code&gt;: 1:47, 4825 LCs.&lt;br&gt;
After using &lt;code&gt;.all()&lt;/code&gt;: 1:44, 4802 LCs.&lt;br&gt;
After fixing our IL digest behaviour: priceless.&lt;/p&gt;
&lt;p&gt;After splitting out just OP_IMM: 404k IL, 2:36, 5038 LCs. O_o&lt;br&gt;
I guess I need to split out the decode a little more? Or maybe it’s just a matter of decomposing more.&lt;/p&gt;
&lt;p&gt;After replacing multiple &lt;code&gt;m.d.sync += self.write_xreg(v_i.rd, ...)&lt;/code&gt; with one of those and a comb wire &lt;code&gt;out&lt;/code&gt; for the value: &lt;strong&gt;404k IL, 1:35, 4851 LCs&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;We’ll split it out as much as possible at first, and then slowly reintegrate. We already do the register save in &lt;code&gt;fetch.init&lt;/code&gt;, and now with some care after splitting out OP_IMM it’s a bit better again.&lt;/p&gt;
&lt;p&gt;Need to remember that the toolchain does &lt;em&gt;much less deduplication than we assume&lt;/em&gt;. Keep going on that, esp with insn decode.&lt;/p&gt;
&lt;p&gt;Using &lt;code&gt;~insn[:16].bool()&lt;/code&gt; instead of &lt;code&gt;== 0&lt;/code&gt;: &lt;strong&gt;404k IL, 1:52, 4800 LCs&lt;/strong&gt;.&lt;br&gt;
Using &lt;code&gt;wb_reg.any()&lt;/code&gt; instead of &lt;code&gt;!= 0&lt;/code&gt;: &lt;strong&gt;no change&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;After splitting out LOAD: &lt;strong&gt;404k IL, 1:59, 4945 LCs&lt;/strong&gt;. Uhm.&lt;br&gt;
After factoring the xreg fetch into common: &lt;strong&gt;402k IL, 1:46, 4972 LCs&lt;/strong&gt;. Hmmmmmm.&lt;/p&gt;
&lt;p&gt;After adding the read register: ran out of BELs. Welp. (6515 cells.)&lt;br&gt;
After changing the read register comb-&amp;gt;sync: 6394 cells. Improved slightly.&lt;br&gt;
After splitting out OP: &lt;strong&gt;371k IL, 7166 cells&lt;/strong&gt;. …&lt;br&gt;
After refactoring OP with &lt;code&gt;out&lt;/code&gt;: 7120 cells.&lt;br&gt;
After splitting out STORE: &lt;strong&gt;368k IL, 7134 cells&lt;/strong&gt;.&lt;br&gt;
After splitting out BRANCH: &lt;strong&gt;343k IL, 6386 cells&lt;/strong&gt;.&lt;br&gt;
After splitting out JALR: &lt;strong&gt;339k IL, 5599 cells and PNR is working again&lt;/strong&gt;.&lt;br&gt;
After changing &lt;code&gt;jump(m, pc)&lt;/code&gt;’s context manager return to &lt;code&gt;~_.bool()&lt;/code&gt;: &lt;strong&gt;339k IL, 5602 cells&lt;/strong&gt;. Uh, ok. Reverting that for now just ‘cause maybe there’s a cross-over point (size of bv).&lt;/p&gt;
&lt;p&gt;Using &lt;code&gt;any()&lt;/code&gt; instead of &lt;code&gt;bool()&lt;/code&gt; causes cell reduction? At 5587. &lt;strong&gt;339k IL, 1:04, 4774 LCs&lt;/strong&gt;.&lt;br&gt;
OK, switching sync-&amp;gt;comb on read regs bumps back up to 5664 (+77), and increases PNR time significantly (maybe because we’re close to cell count?). &lt;strong&gt;338k IL, 2:41, 4984 LCs (94%)&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Next step is to do the instruction decode in one place and then pass info to following stages.&lt;/p&gt;
&lt;p&gt;Added &lt;code&gt;imm&lt;/code&gt; and &lt;code&gt;funct3&lt;/code&gt; to LOAD: &lt;strong&gt;342k IL, 1:06, 4905 LCs (92%)&lt;/strong&gt;.&lt;br&gt;
Did the same to OP_IMM: &lt;strong&gt;342k IL, 0:58, 4886 LCs (92%)&lt;/strong&gt;.&lt;br&gt;
Removed &lt;code&gt;v_sxi&lt;/code&gt; wire and just used &lt;code&gt;imm[:12].as_signed()&lt;/code&gt; in place: &lt;strong&gt;342k IL, 1:01, 4907 LCs (92%)&lt;/strong&gt;.&lt;br&gt;
Did same deal to OP: &lt;strong&gt;343k IL, 1:01, 4806 LCs&lt;/strong&gt;.&lt;br&gt;
Did same deal to STORE: &lt;strong&gt;344k IL, 1:16, 4816 LCs&lt;/strong&gt;. !!!&lt;br&gt;
I forgot to only use the bottom 12 bits of &lt;code&gt;imm&lt;/code&gt;. Fixed: &lt;strong&gt;344k IL, 1:09, 4910 LCs&lt;/strong&gt;. What?&lt;br&gt;
Try doing the sign-extension in resolve: &lt;strong&gt;344k IL, 1:13, 4845 LCs&lt;/strong&gt;.&lt;br&gt;
Do the thing for BRANCH: &lt;strong&gt;337k IL, 1:20, 4859 LCs&lt;/strong&gt;.&lt;br&gt;
JALR too, that’s everything: &lt;strong&gt;337k IL, 1:13, 4812 LCs&lt;/strong&gt;.&lt;br&gt;
Drop &lt;code&gt;v_sxi&lt;/code&gt; and do the sign extension in resolve: &lt;strong&gt;336k IL, 1:15, 4825 LCs&lt;/strong&gt;.&lt;br&gt;
Same for LOAD: &lt;strong&gt;336k IL, 1:17, 4774 LCs (90%)&lt;/strong&gt;. Huh.&lt;/p&gt;
&lt;p&gt;op.op_imm and op.op can be refactored.&lt;br&gt;
Hackily done: &lt;strong&gt;333k IL, 0:59, 4595 LCs&lt;/strong&gt;. OK yeah, that helps!&lt;br&gt;
Done so that it actually works (still hack): &lt;strong&gt;333k IL, 1:05, 4633 LCs (87%)&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Put register file in memory now that it’s all separated out.&lt;br&gt;
(How much is it using, really? Half XCOUNT: &lt;strong&gt;288k IL, 0:19, 3340 LCs (63%)&lt;/strong&gt;. OK, quite a bit.)&lt;/p&gt;
&lt;p&gt;Pico fits in 750–1000. SERV fits in 198??????&lt;/p&gt;
&lt;p&gt;Dumped it in a register file. Gave it two read ports so no existing code has to change, I think it’s just duplicated the memories but they’re so small it doesn’t matter. &lt;strong&gt;265k IL, 0:13, 2367 LCs (44%)&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Cleaned up our reg read and write logic: &lt;strong&gt;264k IL, 0:14, 2300 LCs (43%)&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;TODOs remaining:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;input type="checkbox" checked="" disabled=""&gt; Read all the accepted Amaranth RFCs.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;OK cool.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;input type="checkbox" checked="" disabled=""&gt; Do a once-over and generally clean up the Hart.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;2291 LCs.&lt;br&gt;
2162 LCs after combing the MMU interface.&lt;br&gt;
2269 when I do it to the MMU write port. No point since we have to register a lot anyway. Back to 2162. Similarly it grows when I use comb to set &lt;code&gt;req_width&lt;/code&gt; — maybe because everything else in the FSM (back at this point) is sync, so they all switch together. Hrm.&lt;/p&gt;
&lt;p&gt;Let’s try changing the write_xreg to comb. Basis: &lt;code&gt;8e3c38ca&lt;/code&gt;, 2162 LCs. Hm, nah — this can’t work. We assemble the components over multiple cycles fairly often (use &lt;code&gt;xwr_reg&lt;/code&gt; to store &lt;code&gt;v_X.rd&lt;/code&gt; etc.). What about read_xreg? The result is we &lt;em&gt;must&lt;/em&gt; read the result when expected, since we’re no longer registering the address. I anticipate a growth in LCs (but faster reads).&lt;/p&gt;
&lt;p&gt;2266 LCs, but haven’t removed the extra states yet so tests all fail.&lt;br&gt;
2303 LCs, necessitated an ALU refactor. So I don’t think there’s a benefit to this other than speed? Let’s see how many cycles CXXRTL gains.&lt;br&gt;
57508 after change, 57505 before. Well! The ALU split is something though, if I keep that it’s gonna be uglier anyway. How can I fix up?&lt;/p&gt;
&lt;p&gt;Maybe we can tell the ALU where to read its inputs.&lt;/p&gt;
&lt;p&gt;Adding a top-level “delay” that gates the whole FSM adds 100LCs. It’d be nice to have &lt;em&gt;one&lt;/em&gt; wait state. Ah well.&lt;/p&gt;
&lt;p&gt;2230 LCs after centralising the ALU. Splitting it into two stages gets us to … 2246? Oh. OK. Really didn’t expect that. Guess I won’t do that.&lt;/p&gt;
&lt;p&gt;If we don’t have reg read &lt;em&gt;always on&lt;/em&gt;, we can actually shuffle bits like &lt;code&gt;imm&lt;/code&gt; &lt;em&gt;into&lt;/em&gt; &lt;code&gt;xrd2_val&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Up to 2303 on adding &lt;code&gt;xrd[12]_en&lt;/code&gt;. … and hold the phone, &lt;code&gt;xrd2_val&lt;/code&gt; is comb-driven. Nvm.&lt;br&gt;
It barely helps us anyway since we need to post-process for the ALU. Cancel that.&lt;/p&gt;
&lt;p&gt;2202 after dropping the &lt;code&gt;m.If(funct7[5])&lt;/code&gt; out of the &lt;code&gt;Else()&lt;/code&gt; block — &lt;em&gt;we&lt;/em&gt; know they’re mutually exclusive, whereas it doesn’t.&lt;/p&gt;
&lt;p&gt;On the contrary, 2189 after changing the &lt;code&gt;m.If(funct3 == …ADDSUB): … / m.If(funct3 == ….SR)&lt;/code&gt; to if/elif: easier mux? It resembles a switch (which are probably optimised …).&lt;/p&gt;
&lt;p&gt;2202 after collapsing l.wait states. Cleaner. 2194 after fixing the default bug — that’s 41%.&lt;/p&gt;
&lt;p&gt;I would love to get a better idea &lt;em&gt;where&lt;/em&gt; all these cells are being spent, but it’s pretty hard to say after optimisations.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;input type="checkbox" checked="" disabled=""&gt; Consider the same for the MMU.
&lt;ul&gt;
&lt;li&gt;
&lt;input type="checkbox" checked="" disabled=""&gt; At minimum, use &lt;code&gt;amaranth.lib.stream&lt;/code&gt; for its interface.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;input type="checkbox" checked="" disabled=""&gt; Stop embedding the “address bus” in the MMU along with the UART hook.&lt;/li&gt;
&lt;li&gt;
&lt;input type="checkbox" checked="" disabled=""&gt; Clean up UART.&lt;/li&gt;
&lt;li&gt;
&lt;input type="checkbox" disabled=""&gt; Move onto the big task.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Aside: ABTest&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;I feel like I want to make a little sandbox or something that makes evaluating the RTLIL diff between Amaranth expressions easier (optionally running it through &lt;code&gt;opt&lt;/code&gt; or all the way through synthesis).&lt;/strong&gt; Ideally it could even run in-situ, i.e.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;ABTest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;A&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;comb&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;blah&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;ABTest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;B&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;comb&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;blah&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or even:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;comb&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;blah&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ABTest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;All such sites would be toggled individually (with others defaulting to “A”, no cartesian product) and then outputs presented for comparison.&lt;/p&gt;
&lt;p&gt;Sae’s a bit slow to try this with right now.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="rv32c"&gt;
&lt;h3&gt;RV32C &lt;a href="#rv32c" aria-hidden="true" title="Permalink to section" class="anchor"&gt;🔗&lt;/a&gt; &lt;a href="#top" aria-hidden="true" title="Back to top" class="anchor"&gt;↩&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This will take some re-understanding. We know the shape of the ISA(s) better now so we might be able to design something less Heppin Magic.&lt;/p&gt;
&lt;p&gt;The cherry-picking went fairly straightforwardly, lots of conflicts but all easily resolved. Glad we did it in this order!&lt;/p&gt;
&lt;p&gt;There’s &lt;em&gt;so&lt;/em&gt; much magic in &lt;code&gt;isa.py&lt;/code&gt; that I’m resolved to redesign this in a much more straightforward way.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="design"&gt;
&lt;h4&gt;Design &lt;a href="#design" aria-hidden="true" title="Permalink to section" class="anchor"&gt;🔗&lt;/a&gt; &lt;a href="#top" aria-hidden="true" title="Back to top" class="anchor"&gt;↩&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Users of an ISA defined with this tool:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;assembler/disassembler
&lt;ul&gt;
&lt;li&gt;Opcodes and registers accessible via reflection, including support for defining shorthands (&lt;code&gt;J&lt;/code&gt;, &lt;code&gt;LI&lt;/code&gt;, etc.).&lt;/li&gt;
&lt;li&gt;Need to be able to go the other way, too.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;gateware
&lt;ul&gt;
&lt;li&gt;Clear and easy access to layouts and op constants.&lt;/li&gt;
&lt;li&gt;Exposes metadata like ILEN/XLEN/XCOUNT for gateware to use.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;subclassing ISAs
&lt;ul&gt;
&lt;li&gt;Can be added to, removed from (e.g. RV32E reducing XCOUNT).&lt;/li&gt;
&lt;li&gt;
&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Goals:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Much less magic.
&lt;ul&gt;
&lt;li&gt;Avoid metaclasses, avoid &lt;code&gt;__call__&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Inspecting signatures is OK&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Just enough flexibility to express RV; other ISAs are currently a non-goal.
&lt;ul&gt;
&lt;li&gt;I &lt;em&gt;think&lt;/em&gt; the current design encapsulates most of what we’ll need here.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Notes on the existing design:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Our current design doesn’t include an intermediate representation: &lt;code&gt;IThunk.__call__&lt;/code&gt; winds up calling &lt;code&gt;shape.const().as_value().value&lt;/code&gt;, building up args to pass to &lt;code&gt;const()&lt;/code&gt;; there’s no real point of “calling it done” except for the first time it’s called.&lt;/li&gt;
&lt;li&gt;Many layouts define immediates in groups of &lt;code&gt;imm0_4&lt;/code&gt;, &lt;code&gt;imm5&lt;/code&gt;, &lt;code&gt;imm6_11&lt;/code&gt; kinds of things. Sometimes they also omit e.g. &lt;code&gt;imm0&lt;/code&gt; (implied 0).&lt;/li&gt;
&lt;li&gt;An &lt;code&gt;ISA&lt;/code&gt; has a notion of a &lt;code&gt;Register&lt;/code&gt;, which is a class defined in the return value of &lt;code&gt;ISA.RegisterSpecifier()&lt;/code&gt; (!).
&lt;ul&gt;
&lt;li&gt;This uses &lt;code&gt;locals().update(members)&lt;/code&gt; to define the members of an &lt;code&gt;IntEnum&lt;/code&gt;, where registers is built up from a list of (name, alias0, …, aliasn) tuples and a target size.&lt;/li&gt;
&lt;li&gt;I think we’ll still need something like this; it’s actually one of the least magic parts of this.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;All &lt;code&gt;ISA&lt;/code&gt; members can define &lt;code&gt;_needs_named&lt;/code&gt; and &lt;code&gt;_needs_finalised&lt;/code&gt; attributes, processed in &lt;code&gt;ISAMeta.__new__&lt;/code&gt;.
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;_needs_named&lt;/code&gt; causes the assignment of &lt;code&gt;__name__&lt;/code&gt; and &lt;code&gt;__fullname__&lt;/code&gt; attributes, according to the name being assigned to.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;_needs_finalised&lt;/code&gt; calls &lt;code&gt;finalise&lt;/code&gt; on the object with a reference to the &lt;code&gt;ISA&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;This lets members finish initialising themselves with an awareness of everything else defined in the &lt;code&gt;ISA&lt;/code&gt;, including things defined (lexically) after them.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ILayout&lt;/code&gt; is an empty baseclass with an &lt;code&gt;ILayoutMeta&lt;/code&gt; metaclass.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ILayoutMeta.__new__&lt;/code&gt; takes an optional argument &lt;code&gt;len&lt;/code&gt; and assigns it to &lt;code&gt;cls.len&lt;/code&gt; (where &lt;code&gt;cls&lt;/code&gt; is the newly-created class).&lt;/li&gt;
&lt;li&gt;If &lt;code&gt;layout&lt;/code&gt; is specified, it marks the class as needing finalisation and checks that &lt;code&gt;cls.len&lt;/code&gt; is in fact defined (either now, or in a superclass). Otherwise, it’s considered a layout base class.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ILayoutMeta.finalise&lt;/code&gt;:
&lt;ul&gt;
&lt;li&gt;assembles the defining context dictionary by iterating the &lt;code&gt;ISA&lt;/code&gt;’s MRO backwards for their &lt;code&gt;dir()&lt;/code&gt;s, discounting names starting with underscore (&lt;code&gt;_&lt;/code&gt;);
&lt;ul&gt;
&lt;li&gt;In other words, items in the &lt;code&gt;ISA&lt;/code&gt; class and superclasses define the context for type-shape lookups.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;assembles the full type-shape dictionary by iterating the &lt;code&gt;ILayout&lt;/code&gt; instance’s MRO backwards for annotations, starting from after &lt;code&gt;ISA.ILayout&lt;/code&gt; itself;
&lt;ul&gt;
&lt;li&gt;In other words, annotations in class and its &lt;code&gt;ISA.ILayout&lt;/code&gt; superclasses define the set of type-shapes available to &lt;code&gt;layout&lt;/code&gt; items.&lt;/li&gt;
&lt;li&gt;The context dictionary is used as &lt;code&gt;locals()&lt;/code&gt; here.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;iterates over the &lt;code&gt;layout&lt;/code&gt; tuple given by the subclass, constructing &lt;code&gt;cls._fields&lt;/code&gt; by matching names to &lt;code&gt;ShapeCastable&lt;/code&gt;s:
&lt;ul&gt;
&lt;li&gt;Members can be strings, in which case they refer to an annotation with a matching name.
&lt;ul&gt;
&lt;li&gt;If the exact match lookup is unsuccessful, the class’s &lt;code&gt;resolve()&lt;/code&gt; function is called with some context (the remaining items in the layout, length of instruction remaining needing to be allocated to a field), which must succeed.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Members can be &lt;code&gt;(name, shapecastable)&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;initialises &lt;code&gt;cls.shape = StructLayout(cls._fields)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;initialises &lt;code&gt;cls.values&lt;/code&gt; and &lt;code&gt;cls.defaults&lt;/code&gt; by calling &lt;code&gt;resolve_values&lt;/code&gt; on the class’s existing (set by subclass definition) &lt;code&gt;values&lt;/code&gt; and &lt;code&gt;defaults&lt;/code&gt; members, if any.
&lt;ul&gt;
&lt;li&gt;These may not overlap.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ints&lt;/code&gt; are &lt;code&gt;ints&lt;/code&gt;, strings are treated as keys for the &lt;code&gt;ShapeCastable&lt;/code&gt; for the corresponding field.
&lt;ul&gt;
&lt;li&gt;If item lookup fails, the &lt;code&gt;ShapeCastable&lt;/code&gt;’s &lt;code&gt;__call__&lt;/code&gt; is tried.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ILayoutMeta.resolve&lt;/code&gt; just raises an error. Is this really exposed on subclass instances? Surprising.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ILayoutMeta.xfrm&lt;/code&gt; constructs the class and calls &lt;code&gt;xfrm&lt;/code&gt; on it.
&lt;ul&gt;
&lt;li&gt;If &lt;code&gt;I&lt;/code&gt; is an &lt;code&gt;ILayout&lt;/code&gt; subclass, this just means &lt;code&gt;I.xfrm(…)&lt;/code&gt; is the same as &lt;code&gt;I().xfrm()&lt;/code&gt;, i.e. get an unrefined thunk and then transform it.
&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Digression:&lt;/em&gt; for whatever reason we really like being able to use classes in these positions. It “must” be a class because it’s the result of defining something with &lt;code&gt;class Blah:&lt;/code&gt;, which itself is needed because we often want to supply code, nested classes, etc. But why the insistence on calling the class itself? We don’t &lt;em&gt;ever&lt;/em&gt; have class instances, and doesn’t that seem a bit strange?&lt;/li&gt;
&lt;li&gt;Thinking forward, the class instances should be the intermediate representation, not a separate thunk class. You call &lt;code&gt;I()&lt;/code&gt; or &lt;code&gt;I(a=b)&lt;/code&gt;, you get a &lt;code&gt;&amp;lt;myisa.MyISA.I object&amp;gt;&lt;/code&gt;, with the args hitting &lt;code&gt;I.__init__&lt;/code&gt; like a regular human being.&lt;/li&gt;
&lt;li&gt;This prevents our delightful (…) hack with &lt;code&gt;I(s)&lt;/code&gt;. We can actually just call it &lt;code&gt;I.shape(s)&lt;/code&gt;, which already exists because that’s what it does lol!!&lt;/li&gt;
&lt;li&gt;I have some lingering concerns here around repeated work that currently happens in &lt;code&gt;finalise&lt;/code&gt; etc. but let’s deoptimise now, and reoptimise after the design is sane.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ILayoutMeta.__call__&lt;/code&gt; allows zero or one positional arguments, plus kwargs.
&lt;ul&gt;
&lt;li&gt;In the above example, this is &lt;code&gt;I(…)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Zero positionals asserts a layout is defined, and returns a new &lt;code&gt;IThunk(cls, kwargs)&lt;/code&gt;.
&lt;ul&gt;
&lt;li&gt;In other words, &lt;code&gt;"I(a=b)"&lt;/code&gt;. This denotes a partially refined instruction based on &lt;code&gt;I&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Note that even &lt;code&gt;I()&lt;/code&gt; is valid syntax, to get the same kind of thunk but not refining any part of it.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;One positional asserts a &lt;code&gt;Signal&lt;/code&gt; argument is given and wraps it in the subclass’s &lt;code&gt;shape&lt;/code&gt; (&lt;code&gt;cls.shape(s)&lt;/code&gt;), so you can call &lt;code&gt;I(s)&lt;/code&gt; to decode &lt;code&gt;s&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;IThunk&lt;/code&gt; is as close as we get to an “intermediate representation” here.
&lt;ul&gt;
&lt;li&gt;Sets &lt;code&gt;_needs_named&lt;/code&gt;, as it’s probably going to be assigned in an expression like &lt;code&gt;ADDI = I(funct3=I.IFunct.ADDI)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Stores the class it was constructed from and the &lt;code&gt;kwargs&lt;/code&gt; we got.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;xfrms&lt;/code&gt; initialised to empty.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;asm_args&lt;/code&gt; is defined from &lt;code&gt;list(self.layout)&lt;/code&gt;: it’s the list of arguments an assembly call need to provide. If your layout is &lt;code&gt;("opcode", "rd", "imm")&lt;/code&gt;, we need an opcode, dest register and immediate.
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;opcode&lt;/code&gt; is defined as type &lt;code&gt;Opcode&lt;/code&gt; and &lt;code&gt;rd&lt;/code&gt; as &lt;code&gt;Reg&lt;/code&gt; in the defining context, and &lt;code&gt;imm&lt;/code&gt; is handled in &lt;code&gt;IL.resolve&lt;/code&gt; when it’s in the final position.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;opcode&lt;/code&gt; is refined by being specified in &lt;code&gt;kwargs&lt;/code&gt;, leaving just &lt;code&gt;rd&lt;/code&gt; and &lt;code&gt;imm&lt;/code&gt; for the “asm args”. So how does that happen?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;We iterate over all &lt;code&gt;values&lt;/code&gt; and &lt;code&gt;defaults&lt;/code&gt; in the IL class, and names in &lt;code&gt;kwargs&lt;/code&gt;, removing from &lt;code&gt;asm_args&lt;/code&gt; any specified there.&lt;/li&gt;
&lt;li&gt;Next we iterate names in &lt;code&gt;kwargs&lt;/code&gt;, asserting all specified are a part of the &lt;code&gt;layout&lt;/code&gt;, and none are part of the IL class’s &lt;code&gt;values&lt;/code&gt; (the distinction between &lt;code&gt;values&lt;/code&gt; and &lt;code&gt;defaults&lt;/code&gt; being whether they can be overridden in a thunk ctor or not).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;It has &lt;code&gt;clone()&lt;/code&gt; and &lt;code&gt;partial(**kwargs)&lt;/code&gt;; the former returns a new &lt;code&gt;IThunk&lt;/code&gt; with copies of all settings (for declaration, immutable definition), the latter clones and updates &lt;code&gt;clone.kwargs&lt;/code&gt; with given kwargs, removing those from &lt;code&gt;clone.asm_args&lt;/code&gt; (further refinement of an &lt;code&gt;IThunk&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;It also has &lt;code&gt;xfrm(xfn, **kwarg_defaults)&lt;/code&gt;, which appends a new transform to &lt;code&gt;clone.xfrms&lt;/code&gt;, with some optional default kwargs.
&lt;ul&gt;
&lt;li&gt;Transforms are a function which are handed a set of kwargs, and return a dict to update kwargs given to the next one (or to the &lt;code&gt;ilcls.shape.const(…)&lt;/code&gt; call at the end).&lt;/li&gt;
&lt;li&gt;The kwargs start out as the thunk’s own &lt;code&gt;kwargs&lt;/code&gt; mixed with any given to the &lt;code&gt;IThunk.__call__&lt;/code&gt;, latter superseding the former.&lt;/li&gt;
&lt;li&gt;The transform function’s signature is analysed: if you take a parameter &lt;code&gt;x&lt;/code&gt;, the kwarg &lt;code&gt;x&lt;/code&gt; is filled in (mandatory). If you specify &lt;code&gt;x=default&lt;/code&gt;, then &lt;code&gt;kwarg_defaults&lt;/code&gt; and finally &lt;code&gt;default&lt;/code&gt; are used as fallbacks.
&lt;ul&gt;
&lt;li&gt;I wonder why &lt;code&gt;kwarg_defaults&lt;/code&gt; is only allowed when no default is given. I guess they’re either really mandatory to specify, or possibly optional.&lt;/li&gt;
&lt;li&gt;An example here is &lt;code&gt;shamt_xfrm(shamt, *, imm11_5=0)&lt;/code&gt;. &lt;code&gt;SRAI&lt;/code&gt; overrides this with &lt;code&gt;SRAI = I(funct3=I.IFunct.SRI).xfrm(I.shamt_xfrm, imm11_5=0b0100000)&lt;/code&gt;; the others don’t override it at all.&lt;/li&gt;
&lt;li&gt;In other words, &lt;code&gt;kwarg_defaults&lt;/code&gt; is more like “default overrides”. In either case I don’t imagine a user is actually setting one in a thunk, so maybe they should be treated that way.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;What’s unspecified here is a way for transforms to also transform &lt;code&gt;asm_args&lt;/code&gt;, and that’s where I got up to with&lt;code&gt;# clone.asm_args. ## RESUME XXX GOOD LUCK&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;When an &lt;code&gt;IThunk&lt;/code&gt; is called, we resolve the &lt;code&gt;args_for&lt;/code&gt; the given kwargs.
&lt;ul&gt;
&lt;li&gt;We call the transform pipe with &lt;code&gt;self.kwargs | args&lt;/code&gt;, i.e. those given while constructing the thunk mixed with those given while calling it.&lt;/li&gt;
&lt;li&gt;The result of the pipe is asserted to match the layout and not override anything it’s not allowed to override.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;ilcls.values&lt;/code&gt;, &lt;code&gt;ilcls.defaults&lt;/code&gt; (both already ‘resolved’) and result of resolving the pipe’s output are all combined and become the args passed to &lt;code&gt;shape.const&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Note that transforms are called in the order given, so we must transform &lt;code&gt;asm_args&lt;/code&gt; back-to-front, as inputs used by earlier transforms may be provided by later ones.
&lt;ul&gt;
&lt;li&gt;Actually this is just backwards unless we do yet-more-thunking/accumulating. Let’s reverse the order of how it should be called, so we can apply &lt;code&gt;asm_args&lt;/code&gt; changes as &lt;code&gt;xfrm()&lt;/code&gt; is called repeatedly. Actually call the transforms in reverse order.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
</content>
  </entry>
  <entry>
    <id>tag:lottia.net,2024-07-06:/notes/0008-time-travel.html</id>
    <title type="html">Time travel</title>
    <published>2024-07-06T14:07:00Z</published>
    <updated>2024-07-06T14:07:00Z</updated>
    <link rel="alternate" href="https://un5nvy3mxv5kcnr.julianrbryant.com/notes/0008-time-travel.html" type="text/html"/>
    <content type="html">&lt;section id="top"&gt;
&lt;p&gt;The typical hypothetical “who are you coding for” example meant to shock
you into writing better code is “yourself in six months”, but it turns out
four is completely adequate to get lost.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="start"&gt;
&lt;h2&gt;Start &lt;a href="#start" aria-hidden="true" title="Permalink to section" class="anchor"&gt;🔗&lt;/a&gt; &lt;a href="#top" aria-hidden="true" title="Back to top" class="anchor"&gt;↩&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In February I started writing my first RV32 core, &lt;a href="https://github.com/kivikakk/sae"&gt;Sae&lt;/a&gt;. By the end of the
month, I had enough of one going to run some sample code compiled with GCC for
RV32I, interacting with UART via MMIO, which was such a nice feeling. It was
written in a very dumb manner — meaning it used 95% of the UP5K and took 3
minutes to synthesise — but on the other hand it ran pretty much at one cycle
per cycle! (I hadn’t pipelined instruction memory fetches, so it &lt;em&gt;didn’t&lt;/em&gt;, but
that was the only thing standing in the way of that. It was written very, uh,
plainly.)&lt;/p&gt;
&lt;p&gt;Next I wanted to tackle RV32C, and one thing I wasn’t happy with was &lt;a href="https://github.com/kivikakk/sae/blob/4faae64a780bf0521fc8aa73bf8cc7b70add193c/sae/rtl/rv32.py"&gt;how I was
actually defining the ISA&lt;/a&gt;. It was very manual, and it felt like there
was a lot that could be extracted out in less ad-hoc ways, so that I didn’t then
have to repeat myself even more when adding RV32C, or e.g. making it possible to
define a thing called “RV32E” (which is just RV32I with 16 X registers instead
of 32) as a refinement of RV32I instead of repeating a whole bunch of code, and
then making that selectable as the basis for a core/hart. This also provided an
opportunity to expose more metadata for the built-in assembler and disassembler
used by the test suite.&lt;/p&gt;
&lt;p&gt;I started to extract a bit of a “construction kit” for ISAs, and as was my Python
style at the time, there was a &lt;em&gt;lot&lt;/em&gt; of magic involved. One of the commits describes
redoing something &lt;a href="https://github.com/kivikakk/sae/commit/d23fdd79a948a7cc1f4c1edc94295e4f4cfe6bff"&gt;“with just slightly less magic”&lt;/a&gt;, but iunno, a lot
of magic follows in the commits after that one.&lt;/p&gt;
&lt;p&gt;And then, on March 5, &lt;a href="https://github.com/kivikakk/sae/commit/666247bfea908253d2aa1aa5c2bd8b208fa231fd"&gt;a small “wip” commit&lt;/a&gt; is the last of the branch, with
some code that’s kind of half-there, half-nonsense, and the following intentionally
syntactically incomplete line:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-diff"&gt;             clone.xfrms.append(pipe)
&lt;span class="gi"&gt;+            clone.asm_args. ## RESUME XXX GOOD LUCK&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/section&gt;
&lt;section id="change"&gt;
&lt;h2&gt;Change &lt;a href="#change" aria-hidden="true" title="Permalink to section" class="anchor"&gt;🔗&lt;/a&gt; &lt;a href="#top" aria-hidden="true" title="Back to top" class="anchor"&gt;↩&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;At that stage I was about a month off moving to Estonia, and so all work
stopped. By the time I started to do HDL again, two months later, I’d decided
to stop using Amaranth entirely and
&lt;a href="0007-amaranth-to-chisel.html"&gt;learned to do digital design in Chisel&lt;/a&gt;, which was a big undertaking.
A lot of the machinery around the design needed to be implemented — connecting
to resources on IO pins, the concept of building for different platforms, etc.
— a lot of the toolchain needed to be replaced/rebuilt, but the actual manner
of design changed a lot too. I’d taught myself HDL with Amaranth, and I sort
of retaught myself it with Chisel, grasping the fundamentals more clearly.&lt;/p&gt;
&lt;p&gt;(I also wrote a framework for Chisel in this time, &lt;a href="https://github.com/kivikakk/chryse"&gt;Chryse&lt;/a&gt;, which is where all
my non-project-specific bits went, including the replacements for what Amaranth
gave outside the HDL itself. I had a project framework for Amaranth, &lt;a href="https://github.com/charlottia/hdx#rain"&gt;Rain&lt;/a&gt;, so
Chryse was like Amaranth build’n’platform plus Rain.)&lt;/p&gt;
&lt;p&gt;Three-ish weeks ago, I started using Amaranth again, and it was time to see
where my old work was at. First, I ported everything new from Chryse back to
Rain, freeing it from its weird Nix flake and so giving it a new name, &lt;a href="https://github.com/charlottia/niar"&gt;Niar&lt;/a&gt;.
Then I ported my last Chisel project back to Amaranth, &lt;a href="https://github.com/kivikakk/ili9341spi"&gt;ili9341spi&lt;/a&gt;, using it as
the basis for testing Niar. (It’s just a TFT LCD controller, rendering Conway’s
Game of Life.)&lt;/p&gt;
&lt;p&gt;Once I’d gotten things to my satisfaction, it was time for my next project. I’d
remembered Sae as my last big project before moving/switching off Amaranth, but
my memory didn’t extend much further than that — except maybe the atrocious
build times.&lt;/p&gt;
&lt;p&gt;My next project wants a little CPU core of some kind in it, and I decided to be
responsible and actually bring Sae back to life with my new sensibilities, instead
of just starting fresh and having all that work sit there.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="necromancy"&gt;
&lt;h2 title="Naturally sequent."&gt;Necromancy &lt;a href="#necromancy" aria-hidden="true" title="Permalink to section" class="anchor"&gt;🔗&lt;/a&gt; &lt;a href="#top" aria-hidden="true" title="Back to top" class="anchor"&gt;↩&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There was the &lt;code&gt;main&lt;/code&gt; branch, &lt;a href="https://github.com/kivikakk/sae/commit/f67fec675ff137eba41ca636b9f04d981784a7a1"&gt;last modified February 28&lt;/a&gt;, and then 27
commits of WIP on the &lt;code&gt;rv32c&lt;/code&gt; branch ending in the &lt;code&gt;RESUME XXX GOOD LUCK&lt;/code&gt;
comment. I took one look at the code in that branch and I realised the comment
was extremely apposite. I didn’t have a clue what any of the existing magic
was doing, and I certainly didn’t understand what the final half-commit was
driving towards either. The tests were extremely broken, and so there was
nowhere really to get a foothold on it.&lt;/p&gt;
&lt;p&gt;So it was back to February 28. First I ported it to Niar, which was uneventful,
and then added a &lt;a href="https://github.com/kivikakk/sae/commit/e48cc2778003023fea6037908e0cc3f9b0eb57fb"&gt;test using CXXRTL&lt;/a&gt; to run the same thing we build for
the actual FPGA, which is &lt;a href="https://github.com/kivikakk/sae/blob/4faae64a780bf0521fc8aa73bf8cc7b70add193c/rv/shrimprw.c"&gt;this delightful little program&lt;/a&gt;. Synthesis
still takes 3 minutes, and it turns out all but 10 seconds of this is in
place-and-route. CXXRTL elaboration and compile is much faster, and running real
RV code gives me more certainty than the (extensive but still artificial) unit
tests that I haven’t broken anything.&lt;/p&gt;
&lt;p&gt;At this stage I could’ve considered getting back onto the WIP, but the core
gateware design issue was pretty pressing: no matter how good an experience I
have of specifying an ISA, if I only have 260ish gates free, I’m not doing much
with it, and the build time sucks even if I can mostly test without it. (I don’t
want to be &lt;em&gt;discouraged&lt;/em&gt; from putting it on the FPGA!)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[2024-07-03 13:05:25,148] niar: DEBUG: starting synthesis/pnr
[2024-07-03 13:05:25,148] niar: INFO: [run]   execute_build
[2024-07-03 13:08:12,179] niar: DEBUG: synthesis/pnr finished in 0:02:47.031564
...
[2024-07-03 13:08:12,208] niar: INFO: Device utilisation:
[2024-07-03 13:08:12,208] niar: INFO:            ICESTORM_LC:  5033/ 5280    95%
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So instead I spent time fixing this, which meant separating the core out into
stages, defining an ALU instead of repeating the ops everywhere, putting the
registers into BRAM (that was 40% of the UP5K’s LCs right there), and generally
making the thing slower at runtime and tightening up some definitions in
exchange for 80x faster PNR and using a third of the logic resources. It’s at
least somewhat amenable to pipelining, which is something I’m going to enjoy
implementing later.&lt;/p&gt;
&lt;p&gt;I proceeded methodically and generally did before-and-after builds of every
change, comparing the bytecount of the generated RTLIL for a rough idea of “how
much raw IL does the Amaranth input given produce”, synthesis plus PNR time, and
comparing &lt;code&gt;ICESTORM_LC&lt;/code&gt; counts of the final design (or just cells reported by
Yosys where PNR was failing because the design got too big).&lt;/p&gt;
&lt;p&gt;This is extremely fraught and open to misinterpretation, because Yosys’ output
is optimised, and with many passes involved, there is no direct link between a
given change and the effect on output size. PNR is similar, especially when up
against resource limits, and so the whole thing requires investing not too much
importance in any one result. You’re just kinda vibing it out, making hypotheses
about what might be cheaper post-opt, and then, usually, trying to reason to
yourself why it wasn’t actually cheaper. There’s a lot of holding of judgment
because maybe you need to change &lt;em&gt;all&lt;/em&gt; the things of class X before you can
observe the true difference between two methods.&lt;/p&gt;
&lt;p&gt;A play-by-play of this whole process is included in the raw log with the
subheading &lt;a href="0009-time-travel-raw.html#decombing"&gt;“Decombing”&lt;/a&gt;. At the entry
to this stage, synthesis took 2:47 and PNR completed with 5033/5280 (95%) of
&lt;code&gt;ICESTORM_LC&lt;/code&gt;s used. We finished at synthesis taking 0:10 and using 2194/5280
(41%).&lt;/p&gt;
&lt;/section&gt;
&lt;section id="chrononautics"&gt;
&lt;h2&gt;Chrononautics &lt;a href="#chrononautics" aria-hidden="true" title="Permalink to section" class="anchor"&gt;🔗&lt;/a&gt; &lt;a href="#top" aria-hidden="true" title="Back to top" class="anchor"&gt;↩&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Now we were in a position to start understanding the refactor WIP.&lt;/p&gt;
&lt;p&gt;Step one was rebasing the old branch onto the new. I’d refactored and moved
things around a lot (including e.g. using Amaranth’s new async testbench stuff),
so any commit that touched the existing gateware or testing infrastructure
conflicted, but these were nice little opportunities to understand pieces of
what I was doing in context. The branch itself had a lot of nice refactors
in other areas too, so by the time I’d finished rebasing, I felt in a fairly
good position, except for the fact that neither building it nor running any of
the tests worked because there was a syntax error in the core ISA definition
machinery.&lt;/p&gt;
&lt;p&gt;The next step was to get it running, because I couldn’t hope to complete
whatever it was I was doing in March without understanding any of its context
— namely the entire new ISA kit — and understanding a system is so much
easier when you can see it running.&lt;/p&gt;
&lt;p&gt;To start with, I just commented out the broken line, and it ran, with 44/64
tests passing. Without much in the way of understanding everything around it, I
was able to identify something one specific case needed to happen right at that
same spot,  put together an ultra-specific hack in situ, and we got to 54/64.
At this point, I kind of understand what I was going for, but now I need to
understand everything else to know how to make it happen.&lt;/p&gt;
&lt;p&gt;The way to do that turned out to be just reading the new code over and over
again, outlining it in natural language from the top down. The result of this
process is included in the raw log under the subheading &lt;a href="0009-time-travel-raw.html#design"&gt;“Design”&lt;/a&gt;, and by the
end of it, I’d recapitulated every decision made in coming up with it. The next
steps I’d imagined &lt;a href="https://github.com/kivikakk/sae/commit/456f51b6c0e1aeff93e77b3cf34a5f97f2b2f660"&gt;followed naturally&lt;/a&gt;, and although I entered this stage with
no intention to continue to use this design (on account of its magic-ness), it
still made sense to me to finish my last WIP before turning my mind to simpler
design, since without a fully proven concept I can’t say I’ve grasped it enough
to actually design it again. This got us to 63/64 tests, with the last one being
the first (prospective) RVC test.&lt;/p&gt;
&lt;p&gt;More than proving the concept, though, I just really enjoyed &lt;em&gt;finally&lt;/em&gt;
completing that change, over 4 months later, in a completely different place and
time. Continuity in the face of countless discontinuities.&lt;/p&gt;
&lt;p&gt;Incidentally, this branch doesn’t include much in the way of actual RV32C
support, since I got completely caught up with designing the new ISA kit using
the existing RV32I support as a basis. I think I won’t bother with that, though,
since it’ll likely entail further support from the ISA kit, and in the current
design that means even more magic. Time to simplify.&lt;/p&gt;
&lt;p&gt;&lt;a href="0009-time-travel-raw.html"&gt;Raw notes&lt;/a&gt; follow.&lt;/p&gt;
&lt;/section&gt;
</content>
  </entry>
  <entry>
    <id>tag:lottia.net,2024-05-09:/notes/0007-amaranth-to-chisel.html</id>
    <title type="html">Amaranth to Chisel</title>
    <published>2024-05-09T14:45:00Z</published>
    <updated>2024-06-24T15:00:00Z</updated>
    <link rel="alternate" href="https://un5nvy3mxv5kcnr.julianrbryant.com/notes/0007-amaranth-to-chisel.html" type="text/html"/>
    <content type="html">&lt;section id="top"&gt;
&lt;p&gt;edit: A lot of the following doesn’t apply any more, though it’s all been very
helpful in learning.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;My days of using Amaranth are over.&amp;nbsp;I don’t feel able — nor do I &lt;em&gt;want&lt;/em&gt; — to
depend on something I’m not allowed(!) to contribute to, so I need a way to
continue on with my FPGA studies without it. I don’t really view just trying
to cobble together Verilog as viable for me right now; I’m rather dependent on
having a decent higher-level thing going, and I already feel all the wind sucked
out of my sails from having to make any change whatsoever.&lt;/p&gt;
&lt;p&gt;I’ve experimented with doing my own HDL using what I learned from working with
and on Amaranth (and Yosys, which I’ll happily continue to depend on), but it’s
way too much work. After surveying the scene, I’ve chosen &lt;a href="https://un5gmtkzggy9htkjdenberhh.julianrbryant.com/"&gt;Chisel&lt;/a&gt;. Scala is not
exactly my favourite, and this means really learning it properly, but y’know
what? That’s how I felt about Python too, but I still did &lt;a href="https://github.com/amaranth-lang/amaranth/pull/830"&gt;some cursed stuff&lt;/a&gt;
with it!&lt;/p&gt;
&lt;p&gt;I plan to bootstrap my way out of this hole by creating a small component in
Amaranth, workbench it using CXXRTL, then duplicating that component in Chisel,
using the same CXXRTL workbench to test it. This way I’m staying connected to
“doing useful/measurable stuff” in a way I know. I’m also furthering my own &lt;a href="https://github.com/kivikakk/eri"&gt;HDL
experiments&lt;/a&gt; while I go, letting Amaranth and Chisel combine in my head.&lt;/p&gt;
&lt;p&gt;Done so far:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Bring &lt;a href="https://github.com/charlottia/hdx"&gt;&lt;code&gt;hdx&lt;/code&gt;&lt;/a&gt;, &lt;code&gt;rainhdx&lt;/code&gt;, and all their dependencies — including Amaranth
— up to date.
&lt;ul&gt;
&lt;li&gt;New &lt;code&gt;abc&lt;/code&gt; revision.&lt;/li&gt;
&lt;li&gt;Amaranth depends on a newer &lt;code&gt;pdm-backend&lt;/code&gt;, which I &lt;a href="https://github.com/charlottia/hdx/commit/27c3609f5b90e97ed89ca11a7e5747d4b8d0d90b#diff-14a0b9fe455f18efa8eb5b66ab3f4818d6ef7c32"&gt;needed to
package&lt;/a&gt; since it’s not in nixpkgs.&lt;/li&gt;
&lt;li&gt;Had to unbreak rainhdx’s Nix, that last refactor was bad.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Add &lt;a href="https://github.com/charlottia/hdx/commit/d52075e49ac05a7297b8ed8cd6cdd8a2808e72b0"&gt;basic cxxsim support&lt;/a&gt; to &lt;code&gt;rainhdx&lt;/code&gt;. This was mostly pulled from &lt;a href="https://github.com/charlottia/i2c_obs"&gt;I²C, oh!
Big stretch&lt;/a&gt;, which I maintain is impeccably named.
&lt;ul&gt;
&lt;li&gt;There was also the option to pull the Zig–CXXRTL support from &lt;a href="https://github.com/charlottia/sh1107"&gt;sh1107&lt;/a&gt;, but
the extra toolchain weight doesn’t feel like it helps me move any faster
here.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;A basic &lt;a href="https://github.com/kivikakk/kalaturg/commit/cd7b97cfb697ac7def0d5d0689da9c03f403d3e0"&gt;UART echo&lt;/a&gt;, tested with Amaranth’s simulator.&lt;/li&gt;
&lt;li&gt;A clone of the Python simulator &lt;a href="https://github.com/kivikakk/kalaturg/commit/d4c853a680c494fe9acc36aa91b83a7cd2d4d026"&gt;with CXXRTL&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Learn to do a &lt;a href="https://github.com/kivikakk/kalaturg/commit/35a791d597e0f31a2affda72a9de2c3f21161e36"&gt;very basic Chisel module with tests&lt;/a&gt; and Verilog
output.&lt;/li&gt;
&lt;li&gt;Build the Chisel module with CXXRTL and integrate it into the simulator —
it’ll be very &lt;em&gt;wrong&lt;/em&gt;, but the key is the integration.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/kivikakk/kalaturg/commit/9d704aa2968ab3d287fe23ccfad2bdf26a88d5e3"&gt;Write a little unbuffered UART pair, test them, integrate. &lt;strong&gt;Done.&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Extend the test case to exercise the Amaranth version’s buffers on TX/RX.&lt;/li&gt;
&lt;li&gt;&lt;del&gt;Write a FIFO in Chisel and buffer the TX/RX.&lt;/del&gt;&lt;/li&gt;
&lt;li&gt;Discover &lt;code&gt;Queue&lt;/code&gt; and learn how to use &lt;code&gt;Decoupled&lt;/code&gt; – use that in RX and TX.&lt;/li&gt;
&lt;li&gt;Redo the base UART module using &lt;code&gt;Queue&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Test it on the iCEBreaker!&lt;/li&gt;
&lt;li&gt;Mess around with SB_RGBA_DRV. Buffer the clock input with SB_GB.&lt;/li&gt;
&lt;li&gt;Drop all the Python; it’s no longer necessary.&lt;/li&gt;
&lt;li&gt;Actions CI for unit tests, cxxsim, synthesis.&lt;/li&gt;
&lt;li&gt;Introduce a “Platform” notion to build separately for iCE40 and CXXRTL.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/kivikakk/chryse"&gt;Split off the project-independent bits&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Redo the testbench to have the test unit as a blackboxed instance, rather than
it driving everything through lines from the top. &lt;del&gt;Get it working first with
Amaranth, then Chisel.&lt;/del&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;And now, 12 days later, I’m done! I have a fair bit more ground to re-cover in
terms of (a) actually putting together more complex designs — I’ll start with
an SPI OLED (maaaaaybe with a Zig counterpart, like &lt;a href="https://github.com/charlottia/sh1107"&gt;sh1107&lt;/a&gt;) and then move onto
a RISC-V core again — and (b) creating my own framework to iterate on
different projects quickly, but I’ve moved really fast&lt;sup class="footnote-ref"&gt;&lt;a href="#fn-minimal" id="fnref-minimal" data-footnote-ref=""&gt;1&lt;/a&gt;&lt;/sup&gt; and I’m quite
happy with it.&lt;/p&gt;
&lt;p&gt;It’s been interesting to decompose “HDL and digital design, as I learned it
through Amaranth” — the way I write things has become a lot more fluent, a lot
less “eighty stage FSM”, and I spend a lot more time looking at Verilog, which I
think is a good thing right now. I grok a lot more of what’s under the covers,
especially having to reimplement some of it.&lt;/p&gt;
&lt;p&gt;I can re-archive all my Amaranth stuff again, now that I’ve finished leaning on
it.&lt;/p&gt;
&lt;/section&gt;
&lt;section class="footnotes" data-footnotes="" id="footnotes"&gt;&lt;h2&gt;Footnotes &lt;a href="#footnotes" aria-hidden="true" title="Permalink to section" class="anchor"&gt;🔗&lt;/a&gt; &lt;a href="#top" aria-hidden="true" title="Back to top" class="anchor"&gt;↩&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li id="fn-minimal"&gt;
&lt;p&gt;With minimal tools! At the start of these 12 days I didn’t even have
a desk, or an external monitor. Or a dev board! &lt;a href="#fnref-minimal" class="footnote-backref" data-footnote-backref="" data-footnote-backref-idx="1" aria-label="Back to reference 1"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content>
  </entry>
  <entry>
    <id>tag:lottia.net,2024-01-02:/notes/0006-comrak-on-akkoma.html</id>
    <title type="html">Comrak on Akkoma</title>
    <published>2024-01-02T04:38:00Z</published>
    <updated>2024-01-02T04:38:00Z</updated>
    <link rel="alternate" href="https://un5nvy3mxv5kcnr.julianrbryant.com/notes/0006-comrak-on-akkoma.html" type="text/html"/>
    <content type="html">&lt;section id="top"&gt;
&lt;p&gt;First up: I’ve never done more than toy with Elixir before, and never with Nix
or Rust, so this “simply stuff Nix, Elixir and Rust into a magic hat” trick was
guaranteed to be at least a little bit Fun™. And it was! :)&lt;/p&gt;
&lt;p&gt;Stock Akkoma uses &lt;a href="https://github.com/pragdave/earmark"&gt;Earmark&lt;/a&gt;, which
looks like a lovely library, but maybe a lil out of date and out of step with
CommonMark/GFM. &lt;strong&gt;We deserve &lt;a href="https://github.com/kivikakk/comrak"&gt;Comrak&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Happily enough, a Google search revealed a Nathan Faucett had already done
most of the hard work of using Comrak from Elixir in
&lt;a href="https://un5q025qp1c0.julianrbryant.com/nathanfaucett/ex-markdown"&gt;&lt;code&gt;ex-markdown&lt;/code&gt;&lt;/a&gt;. Thank you! This never gets old.&lt;/p&gt;
&lt;p&gt;Ported it for Comrak and &lt;a href="https://github.com/rusterlium/rustler"&gt;Rustler&lt;/a&gt;
changes in the last 5 years, and then learned about the various ways to
juggle Elixir and Mix releases/deps in Nix. &lt;a href="https://github.com/kivikakk/ex-markdown/compare/master...lottia"&gt;Several hundred lines of hack-ish
later&lt;/a&gt; and
&lt;code&gt;ex-markdown&lt;/code&gt; was now fit for purpose.&lt;/p&gt;
&lt;p&gt;Special care was taken to ensure both &lt;code&gt;nix develop&lt;/code&gt;- and &lt;code&gt;nix build&lt;/code&gt;-based
builds work — this one always wants to be able to quickly iterate in my checkout
without waiting all day for non-incremental builds, but at the end a &lt;code&gt;nix build&lt;/code&gt;
should:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;match the behaviour of a clean &lt;code&gt;nix develop --command bash -c "mix deps.get &amp;amp;&amp;amp; mix test"&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;always cleanly succeed; and,&lt;/li&gt;
&lt;li&gt;run &lt;code&gt;mix test&lt;/code&gt; itself as a post-install check so we don’t get blindsided by
differences in the dev shell/closure-built artefact only when later using it
(i.e. in Akkoma).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This required some finesse: we want to build the native Rust dependency as
usual when doing &lt;code&gt;nix build&lt;/code&gt;, which means doing the usual Cargo/Nix dance
and compiling that artefact as its own derivation (and all its crate deps as
their own, etc. etc.). On the other hand, in &lt;code&gt;nix develop&lt;/code&gt; we want the usual
compile-on-demand to happen. Happily, Rustler is portable enough to support this
workflow! (see the &lt;code&gt;MARKDOWN_NATIVE_SKIP_COMPILATION&lt;/code&gt; env var.)&lt;/p&gt;
&lt;p&gt;One tricky thing is the fucken Mix dependencies. The &lt;code&gt;ex-markdown&lt;/code&gt; derivation
itself needs to introduce its own Mix deps to &lt;code&gt;beamPackages.buildMix&lt;/code&gt; so it can
actually build and test. But that’s no good when we’re building Akkoma — we want
to use a release-wide resolved version of those dependencies, with all BEAM deps
in the one closure and no overlap.&lt;/p&gt;
&lt;p&gt;For now we hack it somewhat, and reproduce some of &lt;code&gt;ex-markdown&lt;/code&gt;’s derivation in
our Akkoma fork — &lt;code&gt;beamPackages&lt;/code&gt; doesn’t have anything like &lt;code&gt;overrideBeamAttrs&lt;/code&gt;
or &lt;code&gt;overrideMixAttrs&lt;/code&gt; at the moment.
&lt;a href="https://github.com/kivikakk/akkoma/compare/v3.10.4...lottia"&gt;There’s a fair bit more Nix&lt;/a&gt;
involved therein.&lt;/p&gt;
&lt;p&gt;We started with upstream Nixpkgs’ Akkoma package definition (again, copying the
original as a base due to lack of override), add our &lt;code&gt;:markdown&lt;/code&gt; package to the
&lt;code&gt;mixNixDeps&lt;/code&gt; — we pull the source, native package and toolchain deps through the
&lt;code&gt;ex-markdown&lt;/code&gt; flake :)! —, adjust the call-sites, and then as a cherry on top,
expose a NixOS module that sets &lt;code&gt;config.services.akkoma.package&lt;/code&gt; to the package
exposed. Using the new Akkoma in my personal config is as simple as referring
the module.&lt;/p&gt;
&lt;p&gt;And there you have it!&lt;/p&gt;
&lt;/section&gt;
&lt;section id="future-work"&gt;
&lt;h2&gt;Future work &lt;a href="#future-work" aria-hidden="true" title="Permalink to section" class="anchor"&gt;🔗&lt;/a&gt; &lt;a href="#top" aria-hidden="true" title="Back to top" class="anchor"&gt;↩&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ex-markdown&lt;/code&gt; only used a native call for parsing the input; the rendering is
done in Elixir. Let’s add the missing NIF for rendering too!&lt;/li&gt;
&lt;li&gt;Working out a nicer way to share the &lt;code&gt;ex-markdown&lt;/code&gt; derivation for use in
downstream projects’ &lt;code&gt;mixNixDeps&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Working out a nicer way to override some properties of Nixpkgs’ Akkoma
derivation.&lt;/li&gt;
&lt;li&gt;Unify version numbers and revisions.&lt;/li&gt;
&lt;li&gt;I’ve just noticed below that Comrak’s (GFM-compliant) autolink feature breaks
remote user refs by turning them into mailto’s! Oops.&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id="reflections"&gt;
&lt;h2&gt;Reflections &lt;a href="#reflections" aria-hidden="true" title="Permalink to section" class="anchor"&gt;🔗&lt;/a&gt; &lt;a href="#top" aria-hidden="true" title="Back to top" class="anchor"&gt;↩&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Having never really touched Elixir much, this was a reasonably intimidating
circus of interdependent parts to dive right into. It was super fun and — as
usual — I credit Nix with making this &lt;em&gt;at all&lt;/em&gt; possible, and more importantly
&lt;em&gt;worthwhile&lt;/em&gt;. The fact that I don’t have to worry about accumulating platform
tools (or getting them installed on the target server etc.) is only a small part
of it.&lt;/p&gt;
&lt;p&gt;I did indeed spend quite a while fucking around with Making All This Shit Work
With Nix, but I’d probably have spent as long or longer if I was just doing
this on some pleb distro because of build artefacts left over from successive
attempts — and of course, most of the work would be rendered null next time I
had to set up a new server! The amount of discovery (and number of dead ends) I
got to rebase into concrete learnings is &lt;em&gt;incredible&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;(I once again express my thanks to those who got me here — especially
&lt;a href="https://un5qee3dggb0.julianrbryant.comcial/@cadey"&gt;@cadey@pony.social&lt;/a&gt; for putting the idea in my head
years ago, and my ex-qpf for using Nix in the year of our lord 2023, which was a
strong enough signal to finally Just Do It.)&lt;/p&gt;
&lt;/section&gt;
</content>
  </entry>
  <entry>
    <id>tag:lottia.net,2023-09-17:/notes/0005-jambalam.html</id>
    <title type="html">Jambalam</title>
    <published>2023-09-17T22:12:00Z</published>
    <updated>2023-09-17T22:12:00Z</updated>
    <link rel="alternate" href="https://un5nvy3mxv5kcnr.julianrbryant.com/notes/0005-jambalam.html" type="text/html"/>
    <content type="html">&lt;p&gt;&lt;a href="https://un5gmtkzgkvecnwrqr1g.julianrbryant.com/watch?v=VeAJ9U5nbVQ"&gt;Have it your way.&lt;/a&gt; (Content note for just about
everything.)&lt;/p&gt;
&lt;p&gt;Further to fish fun in &lt;a href="0003-nix-revisited.html"&gt;Nix refisited&lt;/a&gt;, today I wanted my &lt;code&gt;git log&lt;/code&gt; to work a little different.&lt;/p&gt;
&lt;p&gt;I have &lt;code&gt;l&lt;/code&gt; as an alias for: (I’m going to add a bunch of newlines)&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-shell"&gt;git log
  &lt;span class="nt"&gt;--show-notes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'*'&lt;/span&gt;
  &lt;span class="nt"&gt;--abbrev-commit&lt;/span&gt;
  &lt;span class="nt"&gt;--pretty&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;format:&lt;span class="s1"&gt;'
    %Cred%h
    %Cgreen(%aD)%Creset
    -%C(bold red)%d%Creset
    %s
    %C(bold blue)&amp;lt;%an&amp;gt;%Creset
  '&lt;/span&gt;
  &lt;span class="nt"&gt;--graph&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The output looks like this:&lt;/p&gt;
&lt;p&gt;&lt;img src="assets/gitlog1.png" alt=" A console where “l” has been entered. A commit per line is listed. "&gt;&lt;/p&gt;
&lt;p&gt;I’m focussing right at the end here: &lt;code&gt;&amp;lt;%an&amp;gt;&lt;/code&gt; is what puts &lt;code&gt;&amp;lt;Charlotte&amp;gt;&lt;/code&gt; at the end.
&lt;code&gt;an&lt;/code&gt; stands for “author name”; you can also do &lt;code&gt;ae&lt;/code&gt; for “author email”, and &lt;code&gt;cn&lt;/code&gt; and &lt;code&gt;ce&lt;/code&gt;
for committer name and email respectively.&lt;/p&gt;
&lt;p&gt;Wouldn’t it be nice to see the author &lt;em&gt;and&lt;/em&gt; committer here? Of course, if I just change it to
something like &lt;code&gt;&amp;lt;%an&amp;gt; &amp;lt;%cn&amp;gt;&lt;/code&gt;, we get this: (detacnurt for clarity)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;…0) - (HEAD -&amp;gt; main, origin/main) 0004-happy-birthday: expand. &amp;lt;Charlotte&amp;gt; &amp;lt;Charlotte&amp;gt;
…0) - 0004-happy-birthday: publish. &amp;lt;Charlotte&amp;gt; &amp;lt;Charlotte&amp;gt;
…0) - TODOne. &amp;lt;Charlotte&amp;gt; &amp;lt;Charlotte&amp;gt;
…0) - Rules: relativize paths. &amp;lt;Charlotte&amp;gt; &amp;lt;Charlotte&amp;gt;
…) - TODO &amp;lt;Charlotte&amp;gt; &amp;lt;Charlotte&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the majority of cases, author and committer are the same. GitHub does a neat thing where it
shows the committer (or &lt;code&gt;Co-authored-by:&lt;/code&gt;), but &lt;em&gt;only&lt;/em&gt; if they’re different:&lt;/p&gt;
&lt;p&gt;&lt;img src="assets/githublog.png" alt=" An excerpt of GitHub’s commit view showing one commit listed as “peff authored and gitster committed”, and another as just “gitster committed” "&gt;&lt;/p&gt;
&lt;p&gt;Unfortunately(?), &lt;a href="https://un5q027jw2wt1a8.julianrbryant.com/docs/git-log#_pretty_formats"&gt;git-log’s pretty formats&lt;/a&gt; don’t support “show this field only if it’s different
to that one”. Maybe that’s a good thing. We’re here to break good things.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/charlottia/git/commit/92a62a03692dd1792d5a1c7c0650d5794d25e16f"&gt;Here’s a patch to &lt;code&gt;git&lt;/code&gt; that adds &lt;code&gt;%cm&lt;/code&gt; and &lt;code&gt;%cf&lt;/code&gt;&lt;/a&gt;. The naming semantics are “cute”:
&lt;code&gt;m&lt;/code&gt; and &lt;code&gt;f&lt;/code&gt; are adjacent to &lt;code&gt;n&lt;/code&gt; and &lt;code&gt;e&lt;/code&gt;. The core addition to &lt;code&gt;format_person_part&lt;/code&gt; (grumble) is as
follows:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-c"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;part&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sc"&gt;'m'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;part&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sc"&gt;'f'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;	 &lt;span class="cm"&gt;/* committer name or email if committer != author */&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ca_msg&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;split_ident_line&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;ca_s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ca_msg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ca_len&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ca_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ca_s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name_begin&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;ca_namelen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ca_s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name_end&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;ca_s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name_begin&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;ca_mail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ca_s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mail_begin&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;ca_maillen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ca_s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mail_end&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;ca_s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mail_begin&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;namelen&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;ca_namelen&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
        &lt;span class="n"&gt;maillen&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;ca_maillen&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
        &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;strncmp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ca_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;namelen&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
        &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;strncmp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mail&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ca_mail&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;maillen&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;placeholder_len&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The outer conditional is hit for all &lt;code&gt;%a&lt;/code&gt; and &lt;code&gt;%c&lt;/code&gt; and looks for our &lt;code&gt;m&lt;/code&gt; and &lt;code&gt;f&lt;/code&gt; specifiers; the
middle conditional ensures we’re in &lt;code&gt;%c&lt;/code&gt; and that the Author (!) line can be parsed into name and
mail address. Finally, we check that the Committer name and mail match the Author ones exactly. If
so, we return immediately without appending anything to the output buffer. (We shortly thereafter
treat &lt;code&gt;m&lt;/code&gt; as identical to &lt;code&gt;n&lt;/code&gt; and &lt;code&gt;f&lt;/code&gt; as to &lt;code&gt;e&lt;/code&gt;, in the case where they’re not matching.)&lt;/p&gt;
&lt;p&gt;&lt;code&gt;git show &amp;gt; pretty.c.patch&lt;/code&gt; and &lt;code&gt;pkgs.git.overrideAttrs { patches = [./pretty.c.patch]; }&lt;/code&gt; gets you
the goods. My alias now has &lt;code&gt;&amp;lt;%an&amp;gt;% cm&lt;/code&gt;, meaning it’ll show the committer name (with a space) after
the author name in gtlt, but only if it’s different. Here’s an example of that on &lt;a href="https://github.com/amaranth-lang/amaranth"&gt;Amaranth&lt;/a&gt;’s
repository:&lt;/p&gt;
&lt;p&gt;&lt;img src="assets/gitlog2.png" alt=" A console where “l” been entered; there’s a commit per line, but the first one ends in a name in angle brackets, followed by a different name. The following commits only have one name. "&gt;&lt;/p&gt;
&lt;p&gt;I &lt;em&gt;really&lt;/em&gt; like this. In a tangible sense, my operating systems are becoming &lt;em&gt;mine&lt;/em&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>tag:lottia.net,2023-08-22:/notes/0004-happy-birthday.html</id>
    <title type="html">Happy birthday!</title>
    <published>2023-08-22T05:24:00Z</published>
    <updated>2023-08-24T06:13:00Z</updated>
    <link rel="alternate" href="https://un5nvy3mxv5kcnr.julianrbryant.com/notes/0004-happy-birthday.html" type="text/html"/>
    <content type="html">&lt;section id="opening"&gt;
&lt;p&gt;I’m writing a little hardware I²C clock stretcher (&lt;a href="https://github.com/charlottia/i2c_obs"&gt;I²C, oh! Big stretch&lt;/a&gt;) to
help me make my I²C controller implementation actually support it.&lt;/p&gt;
&lt;p&gt;These are some moments I’ve had while doing so.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="oobe"&gt;
&lt;h2&gt;Out-of-band experience &lt;a href="#oobe" aria-hidden="true" title="Permalink to section" class="anchor"&gt;🔗&lt;/a&gt; &lt;a href="#top" aria-hidden="true" title="Back to top" class="anchor"&gt;↩&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I added a &lt;a href="https://github.com/charlottia/i2c_obs/commit/1078a55c9f5bd63fae9707586393535ed7afcd07#diff-fbbd4dd0ae5cec5958577b18349c32c4e93ed8df0846aacdfe916267b503e6f4"&gt;tiny UART module&lt;/a&gt; to help me debug it. First I emitted a &lt;code&gt;&amp;lt;&lt;/code&gt;
character when starting a big stretch, and a &lt;code&gt;&amp;gt;&lt;/code&gt; character once we’re all
relaxed.&lt;/p&gt;
&lt;p&gt;I was going to then write the number of cycles counted during the SCL tLOW
period in decimal but eventually decided, Who Really Can Be Bothered, and just
shoved it out onto the UART, LSB first.&lt;/p&gt;
&lt;p&gt;The initial test went great:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-console?prompt=&gt;"&gt;&lt;span class="gp"&gt;@vyx ~&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;tio /dev/ttyUSB1 &lt;span class="nt"&gt;-b9600&lt;/span&gt;
&lt;span class="go"&gt;[15:05:11.940] tio v2.5
[15:05:11.940] Press ctrl-t q to quit
[15:05:11.941] Connected
[15:10:05.065] Switched to hexadecimal mode
3c 0f 3e 3c 3e 3c 3e 3c 3e 3c 0f 3e&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;3c&lt;/code&gt; and &lt;code&gt;3e&lt;/code&gt; are &lt;code&gt;&amp;lt;&lt;/code&gt; and &lt;code&gt;&amp;gt;&lt;/code&gt; respectively. You can see I had some fun while
realizing that I need to actually take steps to restart the FSM — disabling it
isn’t enough.&lt;/p&gt;
&lt;p&gt;The I²C bus is running at 400kHz, meaning we expect the SCL low period to last
1/800,000th of a second.&lt;/p&gt;
&lt;p&gt;The iCEBreaker the sleepy kitty is running on is running at 12MHz. At that
speed, 1/800,000th of a second passes in 12,000,000/800,000&lt;sup class="footnote-ref"&gt;&lt;a href="#fn-maths" id="fnref-maths" data-footnote-ref=""&gt;1&lt;/a&gt;&lt;/sup&gt; = 15 cycles.&lt;/p&gt;
&lt;p&gt;And we were seeing &lt;code&gt;0f&lt;/code&gt; in the output, 15! Perfect.&lt;/p&gt;
&lt;p&gt;I recompiled the controller to run at 100kHz and continued the test.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-console"&gt;&lt;span class="go"&gt;3c 3c 3e 3c 3c 3e 3c 3c 3e 3c 3c 3e 3c 3c 3e 3c 3c 3e&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;??? I thought I made a logic error and we were somehow resetting back to the
initial state without finishing measurement.&lt;/p&gt;
&lt;p&gt;And then I said, “don’t fucking tell me,” because it’s not too hard to add &lt;code&gt;0f&lt;/code&gt;
to itself repeatedly in your head and so &lt;code&gt;0f&lt;/code&gt;, &lt;code&gt;1e&lt;/code&gt;, &lt;code&gt;2d&lt;/code&gt;, &lt;code&gt;3c&lt;/code&gt;. Happy birthday!&lt;/p&gt;
&lt;p&gt;At this point I promptly changed the start/stop characters to &lt;code&gt;ff&lt;/code&gt; and &lt;code&gt;fe&lt;/code&gt;, and
then — detecting that I was just setting up the next, much larger footgun for
myself — decided to dump the count one &lt;em&gt;nibble&lt;/em&gt; at a time and thus render any
byte with a non-zero high nibble officially out-of-band, and &lt;a href="https://github.com/charlottia/i2c_obs/commit/da9b89b43319114f3bb0fd43511ae934b10b7fac"&gt;thus&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-console"&gt;&lt;span class="go"&gt;ff 0c 03 fe&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/section&gt;
&lt;section id="measurement"&gt;
&lt;h2&gt;Modes of measurement &lt;a href="#measurement" aria-hidden="true" title="Permalink to section" class="anchor"&gt;🔗&lt;/a&gt; &lt;a href="#top" aria-hidden="true" title="Back to top" class="anchor"&gt;↩&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The I²C controller I’m testing with/for outputs its clock at a 50% duty
cycle exactly&lt;sup class="footnote-ref"&gt;&lt;a href="#fn-fv" id="fnref-fv" data-footnote-ref=""&gt;2&lt;/a&gt;&lt;/sup&gt;. I probably even verified that with a logic analyzer or
oscilloscope at some stage! Point is, my initial idea was to train on the SCL
tLOW period, and then start holding it low for ~twice that period as of the next
low, and thus stretch the clock (a LOT).&lt;/p&gt;
&lt;p&gt;When you’re a noob like me, you may encounter this:&lt;/p&gt;
&lt;p&gt;&lt;img src="assets/halfpull.jpg" alt="a few breadboards with various boards on them, an OLED, a few LEDs, an oscilloscope, way too many cables, a mess)"&gt;&lt;/p&gt;
&lt;p&gt;Here we have the bus only getting pulled up halfway. This is what it looks like
when someone is trying to ground your bus &lt;em&gt;at the same time&lt;/em&gt; as someone trying
to put high out on it. An I²C bus is designed to be &lt;em&gt;pulled&lt;/em&gt; high so that anyone
can pull it low, but we’re seeing it &lt;em&gt;driven&lt;/em&gt; high.&lt;/p&gt;
&lt;p&gt;The controller &lt;a href="https://github.com/charlottia/sh1107/commit/bb7388b9f1a3635711337a304bc17e3c682c8508"&gt;needed&lt;/a&gt; to turn SCL’s output-enable off, to let it get pulled up
high on its own, and then only to switch the output-enable on when it needs to
be driven low. This lets anyone else on the bus keep SCL low, thus stretching
the clock.&lt;/p&gt;
&lt;p&gt;This worked nicely when it comes to letting the bus stretch, but today I was
trying to get the measurement to come out right — I wanted to have the little
debugging app that talks via UART reporting the correct I²C bus speed — and
noticed that, actually, we’re getting it wrong &lt;em&gt;not only because I’m failing to
count correctly&lt;/em&gt;, but because the waveform &lt;em&gt;doesn’t&lt;/em&gt; have a 50% duty cycle!&lt;/p&gt;
&lt;p&gt;My poor baby oscilloscope can’t actually measure fast enough to see it, but
&lt;em&gt;turns out&lt;/em&gt; letting a pull-up resistor bring a bus high takes a little bit
longer than driving it high. As a result, the tail end of the tLOW period is
eating into the start of tHIGH as it shakily makes its way back up to full
voltage. I absolutely need a way to see this, so I need a better scope I guess.&lt;/p&gt;
&lt;p&gt;Anyway, I’ll also measure from rising edge to rising edge (and falling edge to
falling edge), and that should give me some more insight. Logically, I expect
the falling-to-falling to be very consistent, because that transition is driven,
whereas the rising-to-rising might vary depending on when the signal gets high
enough to be considered “high” each cycle.&lt;/p&gt;
&lt;p&gt;I just need to not accidentally reinvent the &lt;a href="https://un5gmtkzgjwww5d6x3ceax70k0.julianrbryant.com/1bitsquared/glasgow"&gt;Glasgow Interface Explorer&lt;/a&gt; while
I’m here.&lt;/p&gt;
&lt;/section&gt;
&lt;section class="footnotes" data-footnotes="" id="footnotes"&gt;&lt;h2&gt;Footnotes &lt;a href="#footnotes" aria-hidden="true" title="Permalink to section" class="anchor"&gt;🔗&lt;/a&gt; &lt;a href="#top" aria-hidden="true" title="Back to top" class="anchor"&gt;↩&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li id="fn-maths"&gt;
&lt;p&gt;1/(12MHz/800kHz) = 1/((1/12,000,000)/(1/800,000)) = 12,000,000/800,000. &lt;a href="#fnref-maths" class="footnote-backref" data-footnote-backref="" data-footnote-backref-idx="1" aria-label="Back to reference 1"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn-fv"&gt;
&lt;p&gt;Hey, that sounds like something I could really formally verify.
I have &lt;a href="https://github.com/charlottia/sh1107/blob/7b05e685eb6ee53b9f069410c9f12005cd580d99/sh1107/formal/__init__.py#L133-L155"&gt;the start&lt;/a&gt; of a verification setup, might as well use it. &lt;a href="#fnref-fv" class="footnote-backref" data-footnote-backref="" data-footnote-backref-idx="2" aria-label="Back to reference 2"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content>
  </entry>
  <entry>
    <id>tag:lottia.net,2023-07-14:/notes/0003-nix-revisited.html</id>
    <title type="html">Nix revisited</title>
    <published>2023-07-14T16:42:00Z</published>
    <updated>2023-07-14T16:42:00Z</updated>
    <link rel="alternate" href="https://un5nvy3mxv5kcnr.julianrbryant.com/notes/0003-nix-revisited.html" type="text/html"/>
    <content type="html">&lt;p&gt;I realized I was in error in not using &lt;a href="https://un5m4by4xjqx6zm5.julianrbryant.com/"&gt;Nix&lt;/a&gt;, and have been addressing that.
(The primary artifact so far that is public is &lt;a href="https://github.com/charlottia/hdx"&gt;&lt;code&gt;hdx&lt;/code&gt;&lt;/a&gt;, a response to
&lt;a href="0001-hdl-toolchain-source.html"&gt;Installing an HDL toolchain from source&lt;/a&gt;.)  I have some knowledge of it from
previous experiments.  Some observations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;You must thread the needle between “properly sitting down and reading the
language guide” and “actively replacing previously-statefully-configured
parts of your system and build environments”.  Without the former none of the
idioms make sense; without the latter you won’t remember anything from the
former.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If you’re fighting Nix, you’re probably missing a good opportunity to use it
instead.  Here’s an example:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;As a heavy Git user, I have a &lt;em&gt;lot&lt;/em&gt; of terse aliases which are part of my
muscle memory.&lt;/li&gt;
&lt;li&gt;It’s unacceptable to me to type &lt;code&gt;git &lt;/code&gt; or even &lt;code&gt;g &lt;/code&gt; before those
aliases, as even the latter represents a 200% additional load on
commands I use extremely frequently.&lt;/li&gt;
&lt;li&gt;It’s preferable to me to use Git’s aliases over shell aliases to do the
actual expansion, particularly as I use some, uh, “Git shell aliases”?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I used to use a method that involved piping the output of this Ruby script
into &lt;a href="https://un5pe0hcw2ujba8.julianrbryant.com/docs/current/cmds/source.html"&gt;&lt;code&gt;source&lt;/code&gt;&lt;/a&gt; in my shell rc:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-ruby"&gt;&lt;span class="c1"&gt;#!/usr/bin/env ruby&lt;/span&gt;

&lt;span class="n"&gt;alias_lines&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sb"&gt;`git config --global --list`&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;grep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^alias\./&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;alias_lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="o"&gt;=~&lt;/span&gt; &lt;span class="sr"&gt;/\Aalias\.([^=]+)=(.*)\n\z/&lt;/span&gt;
  &lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vg"&gt;$1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vg"&gt;$2&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;exp&lt;/span&gt; &lt;span class="o"&gt;=~&lt;/span&gt; &lt;span class="sr"&gt;/\A!/&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"alias &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;git &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;  # &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;exp&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"alias &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;git &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;exp&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(I think the conditional was trying to make up for lack of completions
&lt;em&gt;through&lt;/em&gt; Git aliases, which isn’t necessary these days.)&lt;/p&gt;
&lt;p&gt;I almost took that with me.  Can you believe it?  I now have a
&lt;code&gt;gitAliases.nix&lt;/code&gt; that looks like the following&lt;sup class="footnote-ref"&gt;&lt;a href="#fn-sense" id="fnref-sense" data-footnote-ref=""&gt;1&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-nix"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;co&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"checkout"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nv"&gt;cb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"checkout -b"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nv"&gt;pc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"checkout -p"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nv"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"status -sb"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nv"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"branch"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nv"&gt;ba&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"branch -a"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nv"&gt;bd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"branch -d"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c"&gt;# ...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, in my Home Manager configuration, effectively the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-nix"&gt;&lt;span class="kd"&gt;let&lt;/span&gt;
  &lt;span class="nv"&gt;gitAliases&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="sx"&gt;./gitAliases.nix&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;in&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;home-manager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;users&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;charlotte&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;programs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;fish&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nv"&gt;shellAliases&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c"&gt;# ...&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="kr"&gt;builtins&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;mapAttrs&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;_v&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"git &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;gitAliases&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="nv"&gt;programs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;git&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nv"&gt;aliases&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;gitAliases&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This feels delicious.  I have just noticed I’m not getting &lt;code&gt;--wraps&lt;/code&gt; set when
defining the function, and so completions are not provided.  It appears
slightly unpredictable on fish’s side whether &lt;code&gt;alias x y&lt;/code&gt; will use &lt;code&gt;-w y&lt;/code&gt;,
possibly to do with when in init it’s happening and whether such a function
has been &lt;a href="https://github.com/fish-shell/fish-shell/issues/8395#issuecomment-957135261"&gt;defined before&lt;/a&gt;.  Oh, &lt;a href="https://github.com/fish-shell/fish-shell/blob/861da91bf1029c1442f154f6c369b1b6030b29f3/share/functions/alias.fish#L61-L68"&gt;here we go&lt;/a&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Time to hack that apart.  It’s almost disgusting how easy Nix makes
patching packages I use and then having that just appear on all my
systems!  Fuck!  I’m sure there’s a less nuclear option but I just &lt;em&gt;wanna&lt;/em&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Edit: this is the stuff &lt;a href="https://un5xmzagmmy0enpgt32g.julianrbryant.com/web/20231007165916/https://un5mzxycfjkx6pr.julianrbryant.comk/system/media_attachments/files/110/713/786/757/975/142/original/f04db54777ab7a44.png"&gt;dreams are made of&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;As with anything, keep the stdlib source open in a window/tab/pane.  Here
this means &lt;code&gt;/nix/var/nix/profiles/per-user/root/channels/nixpkgs/&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Builds may &lt;em&gt;not&lt;/em&gt; generally be reproducible between NixOS and Nix on a
different platform.  &lt;a href="https://github.com/charlottia/hdx/commit/b3af8a0bc323931b4866475d72352ea2f00605c1"&gt;Ahem&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I think this implies building &lt;a href="https://github.com/NixOS/nixpkgs/blob/23.05/pkgs/development/embedded/fpga/icestorm/default.nix"&gt;Nixpkgs 23.05’s
icestorm&lt;/a&gt; on macOS today would fail.&lt;/p&gt;
&lt;p&gt;Let’s verify.  We want to use &lt;code&gt;nix-build --option substitute false&lt;/code&gt; to
disable binary substitution, but first invoke the &lt;code&gt;nix-shell&lt;/code&gt; once for the
derivation so we don’t build all its dependencies from source too:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-console"&gt;&lt;span class="gp"&gt;~ $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# After a lot of fucking around with nix-store --gc:&lt;/span&gt;
&lt;span class="gp"&gt;~ $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nix-shell &lt;span class="s1"&gt;'&amp;lt;nixpkgs&amp;gt;'&lt;/span&gt; &lt;span class="nt"&gt;-A&lt;/span&gt; icestorm
&lt;span class="go"&gt;these 16 paths will be fetched (2.62 MiB download, 16.98 MiB unpacked):
  /nix/store/sm3f0jqk0y1bmwpprjy15icb7bw9kfyp-apple-framework-CoreFoundation-11.0.0
  /nix/store/iqh2hzmrnj9rvw6ahdzzsp9cqzf3ji6w-cctools-binutils-darwin-wrapper-973.0.1

[ ... lots of output ... ]

copying path '/nix/store/7v4rbxd8i0hsk2hgy8jnd4qn9vk89a86-clang-wrapper-11.1.0' from 'https://un5nfj1egjpbbbnrx28f6wr.julianrbryant.com'...
copying path '/nix/store/mas4ifv1v6llnqkyxq5w235x0hdq5yq3-stdenv-darwin' from 'https://un5nfj1egjpbbbnrx28f6wr.julianrbryant.com'...

&lt;/span&gt;&lt;span class="gp"&gt;[nix-shell:~]$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;exit&lt;/span&gt;
&lt;span class="go"&gt;exit
&lt;/span&gt;&lt;span class="gp"&gt;~ $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nix-build &lt;span class="nt"&gt;--option&lt;/span&gt; substitute &lt;span class="nb"&gt;false&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;nixpkgs&amp;gt;'&lt;/span&gt; &lt;span class="nt"&gt;-A&lt;/span&gt; icestorm
&lt;span class="go"&gt;this derivation will be built:
  /nix/store/iqw5iqqkm71vx5dl4s6xzpm5ymxjddyq-icestorm-2020.12.04.drv
building '/nix/store/iqw5iqqkm71vx5dl4s6xzpm5ymxjddyq-icestorm-2020.12.04.drv'...

[ ... lots of output ... ]

patching script interpreter paths in /nix/store/jxfzqadgp6ygd0dfdi7s0jx0nwbd3kxh-icestorm-2020.12.04
stripping (with command strip and flags -S) in  /nix/store/jxfzqadgp6ygd0dfdi7s0jx0nwbd3kxh-icestorm-2020.12.04/bin
/nix/store/jxfzqadgp6ygd0dfdi7s0jx0nwbd3kxh-icestorm-2020.12.04
&lt;/span&gt;&lt;span class="gp"&gt;~ $&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It works!  Joke’s on me: the revision used in Nixpkgs is about 8 commits
before the macOS fix that now needs to be worked around.&lt;/p&gt;
&lt;p&gt;Let’s verify this by building the derivation with the revision overridden.
The name is also overridden, to avoid the package name + version being used:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-nix"&gt;&lt;span class="kn"&gt;with&lt;/span&gt; &lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;nixpkgs&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
&lt;span class="nv"&gt;icestorm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;overrideAttrs&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nv"&gt;src&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"icestorm"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nv"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;src&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;override&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;rev&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"d20a5e9001f46262bf0cef220f1a6943946e421d"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;sha256&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;fakeSha256&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We do the little dance to get the fixed-output derivation hash suitable for the
site it’s used:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-console?prompt=$"&gt;&lt;span class="gp"&gt;~ $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nix-build &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s1"&gt;'with import &amp;lt;nixpkgs&amp;gt; {}; icestorm.overrideAttrs ({ src, ... }: { name = "icestorm"; src = src.override { rev = "d20a5e9001f46262bf0cef220f1a6943946e421d"; sha256 = lib.fakeSha256; }; })'&lt;/span&gt;
&lt;span class="go"&gt;these 2 derivations will be built:
  /nix/store/wpcxl7fz89sk1b45xy2m36cv3gljgzmp-source.drv
  /nix/store/gjs2rm2la5as2yh139yaqcz0q5hjgsc7-icestorm.drv
building '/nix/store/wpcxl7fz89sk1b45xy2m36cv3gljgzmp-source.drv'...

trying https://github.com/YosysHQ/icestorm/archive/d20a5e9001f46262bf0cef220f1a6943946e421d.tar.gz
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
100  926k    0  926k    0     0  1195k      0 --:--:-- --:--:-- --:--:-- 5570k
unpacking source archive /private/tmp/nix-build-source.drv-0/d20a5e9001f46262bf0cef220f1a6943946e421d.tar.gz
error: hash mismatch in fixed-output derivation '/nix/store/wpcxl7fz89sk1b45xy2m36cv3gljgzmp-source.drv':
         specified: sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
            got:    sha256-dEBmxO2+Rf/UVyxDlDdJGFAeI4cu1wTCbneo5I4gFG0=
error: 1 dependencies of derivation '/nix/store/gjs2rm2la5as2yh139yaqcz0q5hjgsc7-icestorm.drv' failed to build
&lt;/span&gt;&lt;span class="gp"&gt;~ $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nix-build &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s1"&gt;'with import &amp;lt;nixpkgs&amp;gt; {}; icestorm.overrideAttrs ({ src, ... }: { name = "icestorm"; src = src.override { rev = "d20a5e9001f46262bf0cef220f1a6943946e421d"; sha256 = "dEBmxO2+Rf/UVyxDlDdJGFAeI4cu1wTCbneo5I4gFG0="; }; })'&lt;/span&gt;
&lt;span class="go"&gt;this derivation will be built:
  /nix/store/9klhbj6biiqn9696zzvyy8cmyjfjaw2d-icestorm.drv
building '/nix/store/9klhbj6biiqn9696zzvyy8cmyjfjaw2d-icestorm.drv'...

[ ... lots of output ... ]

       &amp;gt; cp icebox_vlog.py    /nix/store/1rdvdwvz44kkhirbvpn0yx2njwalrbf2-icestorm/bin/icebox_vlog
       &amp;gt; cp icebox_stat.py    /nix/store/1rdvdwvz44kkhirbvpn0yx2njwalrbf2-icestorm/bin/icebox_stat
       &amp;gt; sed -i '' 's+import iceboxdb+import iceboxdb as iceboxdb+g' /nix/store/1rdvdwvz44kkhirbvpn0yx2njwalrbf2-icestorm/bin/icebox.py
       &amp;gt; sed: can't read s+import iceboxdb+import iceboxdb as iceboxdb+g: No such file or directory
       &amp;gt; make[1]: *** [Makefile:65: install] Error 2
       &amp;gt; make[1]: Leaving directory '/private/tmp/nix-build-icestorm.drv-0/source/icebox'
       &amp;gt; make: *** [Makefile:13: install] Error 2
       For full logs, run 'nix-store -l /nix/store/9klhbj6biiqn9696zzvyy8cmyjfjaw2d-icestorm.drv'.
&lt;/span&gt;&lt;span class="gp"&gt;~ $&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sure enough, it does fail.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Nix feels very appropriate for people like me, whose thought processes or
short-term memory may be disturbed without warning, thanks to the nature of
work-in-progress state with declarative systems.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;By which I mean; for the most part, recovering the idea I’m halfway through
an attempt of&lt;sup class="footnote-ref"&gt;&lt;a href="#fn-int" id="fnref-int" data-footnote-ref=""&gt;2&lt;/a&gt;&lt;/sup&gt; is more “image load” than “procedural init”.  It
doesn’t require parsing shell history or terminal scrollback in order to
learn the meaning of the current state of my system—99% of the context is
in a file.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I’m avoiding &lt;code&gt;nix-env&lt;/code&gt; and flakes.  I don’t like the look of workflows that
involve either.  &lt;a href="https://un5v299prjhx7qxx.julianrbryant.com/blog/nix-flakes-1-2022-02-21"&gt;Xe describes&lt;/a&gt; flakes as being suitable for use cases where
you might use Niv or Lorri.  Niv and Lorri also appear to be tools for
workflows I don’t like the look of.  Lorri refers to “fast direnv integration
for robust CLI and editor integration”, and for whatever reason, that’s a
slightly repellent notion to me at this stage.&lt;/p&gt;
&lt;p&gt;I expect my opinion on flakes will change as I continue.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Did I mention builds may not be reproducible?&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-console?prompt=$"&gt;&lt;span class="go"&gt;-- Build files have been written to: /tmp/nix-build-nextpnr.drv-1/nextpnr-54b2045/build
building
build flags: -j10 SHELL=/nix/store/mxvgjwzdvrl81plvgqnzbrqb14ccnji6-bash-5.2-p15/bin/bash
[  0%] Building CXX object bba/CMakeFiles/bbasm.dir/main.cc.o
[  1%] Generating chipdb/chipdb-25k.bba
/nix/store/mxvgjwzdvrl81plvgqnzbrqb14ccnji6-bash-5.2-p15/bin/bash: line 1: 54809 Segmentation fault: 11  /nix/store/zdd58zb8y7bm15jm0985fdjzy8wrmaci-python3-3.11.4/bin/python3.11 /tmp/nix-build-nextpnr.drv-1/nextpnr-54b2045/ecp5/trellis_import.py -L /nix/store/z3mpz8mqd858vbx849zqyh1mdv64l3vd-trellis/lib/trellis -L /nix/store/z3mpz8mqd858vbx849zqyh1mdv64l3vd-trellis/share/trellis/util/common -L /nix/store/z3mpz8mqd858vbx849zqyh1mdv64l3vd-trellis/share/trellis/timing/util -p /tmp/nix-build-nextpnr.drv-1/nextpnr-54b2045/ecp5/constids.inc -g /tmp/nix-build-nextpnr.drv-1/nextpnr-54b2045/ecp5/gfx.h 25k &amp;gt; chipdb/chipdb-25k.bba.new
make[2]: *** [ecp5/CMakeFiles/chipdb-ecp5-bbas.dir/build.make:77: ecp5/chipdb/chipdb-25k.bba] Error 139
make[1]: *** [CMakeFiles/Makefile2:359: ecp5/CMakeFiles/chipdb-ecp5-bbas.dir/all] Error 2
make[1]: *** Waiting for unfinished jobs....
[  2%] Linking CXX executable bbasm
[  2%] Built target bbasm
make: *** [Makefile:136: all] Error 2
error: boost::bad_format_string: format-string is ill-formed
&lt;/span&gt;&lt;span class="gp"&gt;~ $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# ...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(There’s a segmentation fault in there :)))&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;section class="footnotes" data-footnotes="" id="footnotes"&gt;&lt;h2&gt;Footnotes &lt;a href="#footnotes" aria-hidden="true" title="Permalink to section" class="anchor"&gt;🔗&lt;/a&gt; &lt;a href="#top" aria-hidden="true" title="Back to top" class="anchor"&gt;↩&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li id="fn-sense"&gt;
&lt;p&gt;If my internal sense of what a Git alias should be called is occupied by a
base system command that itself is in muscle memory—which only occurs for
2-letter aliases—I transpose the two letters, or repeat a letter somewhere.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;git checkout -p&lt;/code&gt; thus becomes &lt;code&gt;pc&lt;/code&gt; to avoid &lt;code&gt;cp(1)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git cherry-pick&lt;/code&gt; is uncommon enough that it loses the fight for &lt;code&gt;pc&lt;/code&gt;
and gets &lt;code&gt;pcp&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git rm&lt;/code&gt; gets &lt;code&gt;mrm&lt;/code&gt;, because &lt;code&gt;mr&lt;/code&gt; on its own feels like it should be
obviously merge-related — there are 9 aliases beginning with &lt;code&gt;m&lt;/code&gt; that
&lt;em&gt;are&lt;/em&gt; merge-related — but at 3 characters, &lt;code&gt;mrm&lt;/code&gt; is unique enough to
be recognizable.&lt;/li&gt;
&lt;li&gt;Why not &lt;code&gt;cpc&lt;/code&gt; or &lt;code&gt;rmr&lt;/code&gt;?  iirc, &lt;code&gt;checkout -p&lt;/code&gt; got &lt;code&gt;pc&lt;/code&gt; first; when it
was time to introduce a &lt;code&gt;cherry-pick&lt;/code&gt; alias, there was no consideration
of giving it &lt;code&gt;cpc&lt;/code&gt;—&lt;code&gt;pc&lt;/code&gt; was an established metaphor for this
initialism, whereas &lt;code&gt;cpc&lt;/code&gt; would break that and introduce confusion.
Moving &lt;code&gt;checkout -p&lt;/code&gt; to &lt;code&gt;cpc&lt;/code&gt; for consistency’s sake is unacceptable
and leaves no clear answer for &lt;code&gt;cherry-pick&lt;/code&gt;.  &lt;code&gt;rmr&lt;/code&gt; seems fine, but
continuing with the weirdness is what makes a beautiful natural
language :)&lt;/li&gt;
&lt;/ul&gt;
&lt;a href="#fnref-sense" class="footnote-backref" data-footnote-backref="" data-footnote-backref-idx="1" aria-label="Back to reference 1"&gt;↩&lt;/a&gt;
&lt;/li&gt;
&lt;li id="fn-int"&gt;
&lt;p&gt;Which I have to do once every 8–10 minutes on average, at a guess. &lt;a href="#fnref-int" class="footnote-backref" data-footnote-backref="" data-footnote-backref-idx="2" aria-label="Back to reference 2"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content>
  </entry>
  <entry>
    <id>tag:lottia.net,2023-06-29:/notes/0002-untangling-cycles.html</id>
    <title type="html">Untangling cycles</title>
    <published>2023-06-29T00:51:00Z</published>
    <updated>2023-06-29T00:51:00Z</updated>
    <link rel="alternate" href="https://un5nvy3mxv5kcnr.julianrbryant.com/notes/0002-untangling-cycles.html" type="text/html"/>
    <content type="html">&lt;p&gt;This is straight from my journal, so it starts without warning.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;The bit packing is turning out to be surprisingly tricky!&lt;/p&gt;
&lt;p&gt;Memory is synchronous but our uses of &lt;code&gt;addr[0]&lt;/code&gt; were all comb, so they didn’t
align with the actual target in the cycle it got transmitted from memory when we
were advancing &lt;code&gt;addr&lt;/code&gt; every cycle. This was a really good exercise in Being
Confused As Heck.&lt;/p&gt;
&lt;p&gt;Going to try to explicate the above a bit more clearly for my own elucidation.
Ignoring the write half of the equation for simplicity—the issues faced are the
same.&lt;/p&gt;
&lt;p&gt;This post is literate Python. Why not. We have the following as baseline:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;amaranth&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Elaboratable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Memory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Record&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Signal&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;amaranth.build&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Platform&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;amaranth.hdl.ast&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ShapeCastable&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;amaranth.hdl.mem&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ReadPort&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;amaranth.hdl.rec&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;DIR_FANIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DIR_FANOUT&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;amaranth.sim&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Simulator&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ROMBus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Record&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ShapeCastable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ShapeCastable&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;super&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;addr&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DIR_FANIN&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DIR_FANOUT&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ROMBus&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Downstream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Record&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;super&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DIR_FANIN&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stb&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DIR_FANIN&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A &lt;code&gt;ROMBus&lt;/code&gt; is a connectable path to access some read-only memory. &lt;code&gt;Downstream&lt;/code&gt;
here is a hypothetical recipient of data being read from ROM. (The ROM is
actually a RAM that gets filled on power-on from flash.)&lt;/p&gt;
&lt;p&gt;The key problem I was solving was that, until now, I’ve been storing all my data
in 8-bit wide &lt;code&gt;Memory&lt;/code&gt; instances, but a lot of the actual embedded block RAM I’m
using has 16-bit wide words. As a result, the upper 8 bits of every word has
been left unused.&lt;/p&gt;
&lt;p&gt;It’d be nice to add a translation layer that transparently forwarded reads and
writes from an 8-bit addressable space into the 16-bit words. Even bytes in the
lower halves, odd bytes in the upper halves. Here’s what that’d look like:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;&lt;span class="n"&gt;ROM_CONTENT_PACKED&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mh"&gt;0x2211&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x4433&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x6655&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x8877&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;ROM_LENGTH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The length of the ROM that all the downstream consumers care about is the 8-bit
addressable one—address 0 has &lt;code&gt;0x11&lt;/code&gt;, address 1 &lt;code&gt;0x22&lt;/code&gt;, etc. The fact that we
have 8 bytes packed into 4 words of 16 bits is irrelevant to them.&lt;/p&gt;
&lt;p&gt;Here’s where our example will play out:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Example&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Elaboratable&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;downstream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Downstream&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;elaborate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;platform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Platform&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
        &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;Downstream&lt;/code&gt; is exposed on the instance so we can access it from our
simulator process.&lt;/p&gt;
&lt;p&gt;We now need to do the following things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Determine the size of the packed RAM.&lt;/li&gt;
&lt;li&gt;Create our &lt;code&gt;Memory&lt;/code&gt; instance for it.
&lt;ul&gt;
&lt;li&gt;We’ll initialize it with &lt;code&gt;init&lt;/code&gt; here, and completely ignore the write
aspect of the scenario. The issues it will suffer from are the same. (I
sure suffered!)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Get the &lt;code&gt;ReadPort&lt;/code&gt; for our RAM. I’m asserting the lengths here illustratively for the reader’s benefit.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;        &lt;span class="n"&gt;packed_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ceil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ROM_LENGTH&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;rom_mem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Memory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;depth&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;packed_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ROM_CONTENT_PACKED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;submodules&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rom_rd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rom_rd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rom_mem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_port&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rom_rd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rom_rd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;rom_rd.addr&lt;/code&gt; determines the address in the 16-bit-wide RAM (&lt;code&gt;0x0&lt;/code&gt;–&lt;code&gt;0x3&lt;/code&gt;), and
&lt;code&gt;rom_rd.data&lt;/code&gt; returns those 16 bits. &lt;code&gt;Memory&lt;/code&gt; is &lt;a href="https://github.com/amaranth-lang/amaranth/blob/99417d6499b006a172d5b8cba413fd6181737374/amaranth/hdl/mem.py#L153"&gt;synchronous by
default&lt;/a&gt;
(and the read enable is also &lt;a href="https://github.com/amaranth-lang/amaranth/blob/99417d6499b006a172d5b8cba413fd6181737374/amaranth/hdl/mem.py#L165-L169"&gt;always on under default
settings&lt;/a&gt;),
so, given a made-up &lt;code&gt;mem[x]&lt;/code&gt; operator, the following timeline applies:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;cycle &lt;em&gt;n&lt;/em&gt;+0: some process assigns &lt;code&gt;rom_rd.addr.eq(x)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;cycle &lt;em&gt;n&lt;/em&gt;+1: the read port sees its new &lt;code&gt;addr&lt;/code&gt; value and assigns
&lt;code&gt;rom_rd.data.eq(mem[x])&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;cycle &lt;em&gt;n&lt;/em&gt;+2: &lt;code&gt;rom_rd.data&lt;/code&gt; takes the value of &lt;code&gt;mem[x]&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now we’ll create our &lt;code&gt;ROMBus&lt;/code&gt;. This is what all the RTL I had was already
using—it was connected directly to the read port of the 8-wide memory.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;        &lt;span class="n"&gt;rom_bus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ROMBus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ROM_LENGTH&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rom_bus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rom_bus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We’re going to put the actual translation logic and state machine in separate
functions, so they can be changed later while preserving the literacy of this
post. &lt;em&gt;Why not&lt;/em&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;translation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rom_rd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rom_bus&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fsm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rom_bus&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We want to hook up the ROM bus to the memory in a transparent fashion. Here’s
what I started with:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;translation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rom_rd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ReadPort&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rom_bus&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ROMBus&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;comb&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="n"&gt;rom_rd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rom_bus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;rom_bus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;rom_rd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;word_select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rom_bus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;We shift off the last bit of the input (8-bit) address to create the output
(16-bit) address, creating the following mapping:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align="right"&gt;8-bit address&lt;/th&gt;
&lt;th align="right"&gt;16-bit address&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align="right"&gt;
&lt;code&gt;0x0&lt;/code&gt; / &lt;code&gt;0b000&lt;/code&gt;
&lt;/td&gt;
&lt;td align="right"&gt;
&lt;code&gt;0x0&lt;/code&gt; / &lt;code&gt;0b00&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="right"&gt;
&lt;code&gt;0x1&lt;/code&gt; / &lt;code&gt;0b001&lt;/code&gt;
&lt;/td&gt;
&lt;td align="right"&gt;
&lt;code&gt;0x0&lt;/code&gt; / &lt;code&gt;0b00&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="right"&gt;
&lt;code&gt;0x2&lt;/code&gt; / &lt;code&gt;0b010&lt;/code&gt;
&lt;/td&gt;
&lt;td align="right"&gt;
&lt;code&gt;0x1&lt;/code&gt; / &lt;code&gt;0b01&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="right"&gt;
&lt;code&gt;0x3&lt;/code&gt; / &lt;code&gt;0b011&lt;/code&gt;
&lt;/td&gt;
&lt;td align="right"&gt;
&lt;code&gt;0x1&lt;/code&gt; / &lt;code&gt;0b01&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="right"&gt;
&lt;code&gt;0x4&lt;/code&gt; / &lt;code&gt;0b100&lt;/code&gt;
&lt;/td&gt;
&lt;td align="right"&gt;
&lt;code&gt;0x2&lt;/code&gt; / &lt;code&gt;0b10&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="right"&gt;
&lt;code&gt;0x5&lt;/code&gt; / &lt;code&gt;0b101&lt;/code&gt;
&lt;/td&gt;
&lt;td align="right"&gt;
&lt;code&gt;0x2&lt;/code&gt; / &lt;code&gt;0b10&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="right"&gt;
&lt;code&gt;0x6&lt;/code&gt; / &lt;code&gt;0b110&lt;/code&gt;
&lt;/td&gt;
&lt;td align="right"&gt;
&lt;code&gt;0x3&lt;/code&gt; / &lt;code&gt;0b11&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="right"&gt;
&lt;code&gt;0x7&lt;/code&gt; / &lt;code&gt;0b111&lt;/code&gt;
&lt;/td&gt;
&lt;td align="right"&gt;
&lt;code&gt;0x3&lt;/code&gt; / &lt;code&gt;0b11&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;We select the 8-bit word from the 16-bit data coming out of the memory corresponding to the LSB of the input (8-bit) address.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;a.word_select(b, w)&lt;/code&gt; is essentially &lt;code&gt;a[b*w : (b+1)*w]&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;When the LSB of the 8-bit address is 0, this will select &lt;code&gt;rd_data[0:8]&lt;/code&gt;.
When the LSB is 1, this will select &lt;code&gt;rd_data[8:16]&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;So:
&lt;ul&gt;
&lt;li&gt;8-bit address &lt;code&gt;0x0&lt;/code&gt; will select &lt;code&gt;mem[0x0][0:8]&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;8-bit address &lt;code&gt;0x1&lt;/code&gt; will select &lt;code&gt;mem[0x0][8:16]&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;8-bit address &lt;code&gt;0x2&lt;/code&gt; will select &lt;code&gt;mem[0x1][0:8]&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;8-bit address &lt;code&gt;0x3&lt;/code&gt; will select &lt;code&gt;mem[0x1][8:16]&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now we implement a reader from our ROM:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fsm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rom_bus&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ROMBus&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sync&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;downstream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;FSM&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;State&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;INITIAL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="c1"&gt;# cycle n+0
&lt;/span&gt;                &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sync&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;rom_bus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;next&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;WAIT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

            &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;State&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;WAIT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="c1"&gt;# cycle n+1 / n'+1
&lt;/span&gt;                &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;next&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;READ&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

            &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;State&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;READ&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="c1"&gt;# cycle n+2, n'+0
&lt;/span&gt;                &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sync&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;downstream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rom_bus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;downstream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                    &lt;span class="n"&gt;rom_bus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rom_bus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;next&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;WAIT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is a simple process that reads data and passes them along to some
downstream process (which needs to be able to accept this data as fast as we
give it to them!).&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We start at address zero (&lt;em&gt;n&lt;/em&gt;+0),&lt;/li&gt;
&lt;li&gt;wait a cycle for the memory to see it (&lt;em&gt;n&lt;/em&gt;+1),&lt;/li&gt;
&lt;li&gt;and then pass it to the downstream (&lt;em&gt;n&lt;/em&gt;+2) while advancing the address we read
(&lt;em&gt;n&lt;/em&gt;’+0).&lt;/li&gt;
&lt;li&gt;The next cycle we’re back in &lt;code&gt;WAIT&lt;/code&gt; as advanced address is seen by the memory
(&lt;em&gt;n&lt;/em&gt;’+1).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We end up strobing the downstream every other cycle. (That strobe is seen in the
&lt;em&gt;n&lt;/em&gt;+1 / &lt;em&gt;n&lt;/em&gt;’+1 cycle.)&lt;/p&gt;
&lt;p&gt;Let’s simulate it and report the results:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;dut&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Example&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;dut&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;downstream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stb&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;dut&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;downstream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;02&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="k"&gt;return&lt;/span&gt;
            &lt;span class="k"&gt;yield&lt;/span&gt;

    &lt;span class="n"&gt;sim&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Simulator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dut&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;sim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_clock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1e-6&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;sim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_sync_process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;sim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This can now be run:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-console"&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;python &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'import ex; ex.main()'&lt;/span&gt;
&lt;span class="go"&gt;data: 11
data: 22
data: 33
data: 44
data: 55
data: 66
data: 77
data: 88&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It’s perfect!&lt;/p&gt;
&lt;p&gt;Almost. Let’s revisit the timeline for accessing the synchronous memory:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;cycle &lt;em&gt;n&lt;/em&gt;+0: &lt;code&gt;rom_rd.addr.eq(x)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;cycle &lt;em&gt;n&lt;/em&gt;+1: read port sees new &lt;code&gt;addr&lt;/code&gt;, assigns &lt;code&gt;rom_rd.data.eq(mem[x])&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;cycle &lt;em&gt;n&lt;/em&gt;+2: &lt;code&gt;rom_rd.data&lt;/code&gt; sees &lt;code&gt;mem[x]&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The important part is that you can assign a new address &lt;code&gt;y&lt;/code&gt; in cycle &lt;em&gt;n&lt;/em&gt;+1,
without impacting what happens in cycle &lt;em&gt;n&lt;/em&gt;+2, such that &lt;code&gt;mem[y]&lt;/code&gt; is now
available to use in cycle &lt;em&gt;n&lt;/em&gt;+3. The read port will only see the address &lt;code&gt;y&lt;/code&gt; in
the same cycle that it’s already propagated &lt;code&gt;mem[x]&lt;/code&gt; into its data register.&lt;/p&gt;
&lt;p&gt;Let’s now change our state machine to take advantage of this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fsm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Example&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rom_bus&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ROMBus&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sync&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;downstream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;FSM&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;State&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;INITIAL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="c1"&gt;# cycle n+0
&lt;/span&gt;            &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sync&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;rom_bus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;next&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;WAIT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;State&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;WAIT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="c1"&gt;# cycle n+1, n'+0
&lt;/span&gt;            &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sync&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;rom_bus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;next&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;READ&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;State&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;READ&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="c1"&gt;# cycle n+2, n'+1, n''+0
&lt;/span&gt;            &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sync&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;downstream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rom_bus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;downstream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="n"&gt;rom_bus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rom_bus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="p"&gt;]&lt;/span&gt;


&lt;span class="n"&gt;Example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fsm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fsm&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;We start at address zero (&lt;em&gt;n&lt;/em&gt;+0),&lt;/li&gt;
&lt;li&gt;while waiting a cycle for the memory to see it (&lt;em&gt;n&lt;/em&gt;+1), we also increment the
address to one (&lt;em&gt;n&lt;/em&gt;’+0),&lt;/li&gt;
&lt;li&gt;and then pass the first result the downstream (&lt;em&gt;n&lt;/em&gt;+2), while the memory is
just now seeing the second result (&lt;em&gt;n&lt;/em&gt;’+1), and simultaneously increment the
address we read (&lt;em&gt;n&lt;/em&gt;’’+0).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We don’t change state once we’re in &lt;code&gt;READ&lt;/code&gt;: every cycle we hand to downstream
the data from the address we set two cycles ago; every cycle the memory is
seeing the address we gave one cycle ago; every cycle we increment the address
to keep it going.&lt;/p&gt;
&lt;p&gt;(My wording here muddles up the timing of when we “set” a given value quite a
lot — really, we &lt;em&gt;initiate&lt;/em&gt; the setting of the address two cycles ago, which one
cycle ago &lt;em&gt;is&lt;/em&gt; set (and seen), which this cycle we then see the data returned
of.)&lt;/p&gt;
&lt;p&gt;This is pretty theoretical in this form, but I have a few state machines that do
this kind of sliding continuous read in a limited fashion.&lt;/p&gt;
&lt;p&gt;So what happens?&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-console"&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;python &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'import ex; ex.main()'&lt;/span&gt;
&lt;span class="go"&gt;data: 22
data: 11
data: 44
data: 33
data: 66
data: 55
data: 88
data: 77&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;All the bytes are reversed! (This was a &lt;em&gt;lot&lt;/em&gt; weirder to debug when the same
problem might have been affecting the initial write to RAM, too.)&lt;/p&gt;
&lt;p&gt;Why?&lt;/p&gt;
&lt;p&gt;We’ll review the translation statements:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;comb&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;rom_rd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rom_bus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;rom_bus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;rom_rd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;word_select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rom_bus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This translation happens in the combinatorial domain, meaning that &lt;code&gt;rom_rd.addr&lt;/code&gt;
will change to &lt;code&gt;rom_bus.addr &amp;gt;&amp;gt; 1&lt;/code&gt; as soon as a change on &lt;code&gt;rom_bus.addr&lt;/code&gt; is
registered — there isn’t an additional cycle between the requested 8-bit address
on the ROM bus changing and the read port’s 16-bit address changing:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align="right"&gt;cycle&lt;/th&gt;
&lt;th align="right"&gt;statement issued&lt;/th&gt;
&lt;th align="right"&gt;
&lt;nobr&gt;ROM bus&lt;/nobr&gt; addr&lt;/th&gt;
&lt;th align="right"&gt;
&lt;nobr&gt;read port&lt;/nobr&gt; addr&lt;/th&gt;
&lt;th align="right"&gt;
&lt;nobr&gt;read port&lt;/nobr&gt; data&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align="right"&gt;0&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;rom_bus.addr.eq(0)&lt;/code&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;em&gt;x&lt;/em&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;em&gt;x&lt;/em&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;em&gt;x&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="right"&gt;1&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;rom_bus.addr.eq(1)&lt;/code&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;0&lt;/code&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;0&lt;/code&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;em&gt;x&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="right"&gt;2&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;rom_bus.addr.eq(2)&lt;/code&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;1&lt;/code&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;0&lt;/code&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;0x2211&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="right"&gt;3&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;rom_bus.addr.eq(3)&lt;/code&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;2&lt;/code&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;1&lt;/code&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;0x2211&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="right"&gt;4&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;rom_bus.addr.eq(4)&lt;/code&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;3&lt;/code&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;1&lt;/code&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;0x4433&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="right"&gt;5&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;rom_bus.addr.eq(5)&lt;/code&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;4&lt;/code&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;2&lt;/code&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;0x4433&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Similarly, the ROM bus data port will be updated as soon as the read port’s data
port (&lt;code&gt;rom_rd.data&lt;/code&gt;) changes.&lt;/p&gt;
&lt;p&gt;It will &lt;em&gt;also&lt;/em&gt; be updated as soon as the LSB of the ROM bus’s requested address
changes (&lt;code&gt;rom_bus.addr[0]&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;But by the time we’re actually getting data in the read port for an address, the
ROM bus has registered the next address!  Thus we select the half of the 16-bit
word based on the LSB of the &lt;em&gt;following&lt;/em&gt; address, which (given the addresses are
sequential) will always be the opposite half to the one we really want:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align="right"&gt;cycle&lt;/th&gt;
&lt;th align="right"&gt;
&lt;nobr&gt;ROM bus&lt;/nobr&gt; addr&lt;/th&gt;
&lt;th align="right"&gt;
&lt;nobr&gt;read port&lt;/nobr&gt; data&lt;/th&gt;
&lt;th align="right"&gt;
&lt;nobr&gt;ROM bus&lt;/nobr&gt; &lt;nobr&gt;addr [0]&lt;/nobr&gt;
&lt;/th&gt;
&lt;th align="right"&gt;
&lt;nobr&gt;ROM bus&lt;/nobr&gt; data&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align="right"&gt;0&lt;/td&gt;
&lt;td align="right"&gt;&lt;em&gt;x&lt;/em&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;em&gt;x&lt;/em&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;em&gt;x&lt;/em&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;em&gt;x&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="right"&gt;1&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;0&lt;/code&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;em&gt;x&lt;/em&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;0&lt;/code&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;em&gt;x&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="right"&gt;2&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;1&lt;/code&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;0x2211&lt;/code&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;1&lt;/code&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;0x22&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="right"&gt;3&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;2&lt;/code&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;0x2211&lt;/code&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;0&lt;/code&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;0x11&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="right"&gt;4&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;3&lt;/code&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;0x4433&lt;/code&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;1&lt;/code&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;0x44&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="right"&gt;5&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;4&lt;/code&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;0x4433&lt;/code&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;0&lt;/code&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;0x33&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;We need to introduce a delay in the address as used by the translation on the
way back out, to account for the fact that read data corresponds to the address
from the previous registered cycle, not this one:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;translation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Example&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;rom_rd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ReadPort&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;rom_bus&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ROMBus&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;last_addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Signal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;like&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rom_bus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sync&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;last_addr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rom_bus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;comb&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;rom_rd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rom_bus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;rom_bus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rom_rd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;word_select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;last_addr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;


&lt;span class="n"&gt;Example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;translation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;translation&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This gives:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align="right"&gt;cycle&lt;/th&gt;
&lt;th align="right"&gt;
&lt;nobr&gt;ROM bus&lt;/nobr&gt; addr&lt;/th&gt;
&lt;th align="right"&gt;last &lt;nobr&gt;ROM bus&lt;/nobr&gt; addr&lt;/th&gt;
&lt;th align="right"&gt;
&lt;nobr&gt;read port&lt;/nobr&gt; data&lt;/th&gt;
&lt;th align="right"&gt;last &lt;nobr&gt;ROM bus&lt;/nobr&gt; &lt;nobr&gt;addr [0]&lt;/nobr&gt;
&lt;/th&gt;
&lt;th align="right"&gt;
&lt;nobr&gt;ROM bus&lt;/nobr&gt; data&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align="right"&gt;0&lt;/td&gt;
&lt;td align="right"&gt;&lt;em&gt;x&lt;/em&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;em&gt;x&lt;/em&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;em&gt;x&lt;/em&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;em&gt;x&lt;/em&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;em&gt;x&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="right"&gt;1&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;0&lt;/code&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;em&gt;x&lt;/em&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;em&gt;x&lt;/em&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;em&gt;x&lt;/em&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;em&gt;x&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="right"&gt;2&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;1&lt;/code&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;0&lt;/code&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;0x2211&lt;/code&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;0&lt;/code&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;0x11&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="right"&gt;3&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;2&lt;/code&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;1&lt;/code&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;0x2211&lt;/code&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;1&lt;/code&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;0x22&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="right"&gt;4&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;3&lt;/code&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;2&lt;/code&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;0x4433&lt;/code&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;0&lt;/code&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;0x33&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="right"&gt;5&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;4&lt;/code&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;3&lt;/code&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;0x4433&lt;/code&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;1&lt;/code&gt;&lt;/td&gt;
&lt;td align="right"&gt;&lt;code&gt;0x44&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;And so:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-console"&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;python &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'import ex; ex.main()'&lt;/span&gt;
&lt;span class="go"&gt;data: 11
data: 22
data: 33
data: 44
data: 55
data: 66
data: 77
data: 88&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I like how the &lt;em&gt;x&lt;/em&gt;’s in this table don’t flow back “up” in time as the data
dependencies flow right, whereas in the previous table, they do.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>tag:lottia.net,2023-06-27:/notes/0001-hdl-toolchain-source.html</id>
    <title type="html">Installing an HDL toolchain from source</title>
    <published>2023-06-27T09:12:00Z</published>
    <updated>2023-06-27T09:12:00Z</updated>
    <link rel="alternate" href="https://un5nvy3mxv5kcnr.julianrbryant.com/notes/0001-hdl-toolchain-source.html" type="text/html"/>
    <content type="html">&lt;section id="opening"&gt;
&lt;p&gt;It occurred to me while writing up &lt;a href="https://github.com/charlottia/sh1107/tree/aeb1c3f77d3226760755331624dd7920779cc2b7#requirements"&gt;§ Requirements&lt;/a&gt; in the README
for some&lt;sup class="footnote-ref"&gt;&lt;a href="#fn-baby" id="fnref-baby" data-footnote-ref=""&gt;1&lt;/a&gt;&lt;/sup&gt; gateware that getting the whole beginner’s open-source FPGA
toolchain set up can be a serious stumbling block, as I imagine it probably was
for me once.&lt;/p&gt;
&lt;p&gt;The main pre-packaged solution that comes to mind is &lt;a href="https://github.com/YosysHQ/oss-cad-suite-build"&gt;OSS CAD Suite&lt;/a&gt;. It’s
excellent, but common to “all-in-one” solutions, it makes assumptions that mean
it’s prone to inflexibility in certain ways. Rebuilding just one of the tools is
not always as simple as that — Python environment or shared library conflicts
can result, and the tools are wrapped so as to always prefer their own
distribution, necessitating further hacks for those that call each other.&lt;/p&gt;
&lt;p&gt;With any luck, you won’t run into any of these cases, but if you do, getting
everything built for yourself correctly can be a bit vexing — the YosysHQ tools’
documentation tends to point back to their own pre-built packages (and products)
in preference to (and sometimes instead of) instructing on how to build.&lt;/p&gt;
&lt;p&gt;I’ve done this process three times recently while rejigging my development
environments, so I’m describing it for posterity/others.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="scope"&gt;
&lt;h2&gt;Scope &lt;a href="#scope" aria-hidden="true" title="Permalink to section" class="anchor"&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;By the end of this guide, you’ll have the following ready to go:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://un5q027jw2wt1a8.julianrbryant.com/"&gt;Git&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://un5gmtkzgjcywhd1hkae4.julianrbryant.com"&gt;Python 3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://un5mz2nwqavq3qfahkae4.julianrbryant.com/docs/amaranth/latest/intro.html"&gt;Amaranth&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://un5hg89mq6quzapfhhuxm.julianrbryant.com/yosys/"&gt;Yosys&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/YosysHQ/nextpnr"&gt;nextpnr&lt;/a&gt; with &lt;a href="https://github.com/YosysHQ/icestorm"&gt;Project IceStorm&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/YosysHQ/sby"&gt;SymbiYosys&lt;/a&gt; and &lt;a href="https://github.com/Z3Prover/z3"&gt;Z3&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href="https://un5q027jw2wt1a8.julianrbryant.com/"&gt;Git&lt;/a&gt; is for acquiring source code.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://un5gmtkzgjcywhd1hkae4.julianrbryant.com"&gt;Python 3&lt;/a&gt; is for using Amaranth.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://un5mz2nwqavq3qfahkae4.julianrbryant.com/docs/amaranth/latest/intro.html"&gt;Amaranth&lt;/a&gt; is the &lt;a href="https://un5qgjbzw9dxcq3ecfxberhh.julianrbryant.com/wiki/Hardware_description_language"&gt;HDL&lt;/a&gt; I’m using. It is a Python library which consists of a
language for describing digital logic, as well as facilitating simulation and
building of the resulting designs. It integrates well with the ecosystem, and
permits intermixing with &lt;a href="https://un5qgjbzw9dxcq3ecfxberhh.julianrbryant.com/wiki/Verilog"&gt;Verilog&lt;/a&gt; (or &lt;a href="https://un5qgjbzw9dxcq3ecfxberhh.julianrbryant.com/wiki/VHDL"&gt;VHDL&lt;/a&gt;). At time of writing, its
development pace has quickened.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://un5hg89mq6quzapfhhuxm.julianrbryant.com/yosys/"&gt;Yosys&lt;/a&gt; is the synthesis framework at the heart of Amaranth. It is a digital
logic synthesizer, which is a phrasing that severely understates how much work
is involved—for more information, see the &lt;a href="https://un5hg89mq6qx6xapwfkdyn001cf0.julianrbryant.com/_/downloads/en/latest/pdf/"&gt;Yosys manual&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Amaranth actually comes with its own &lt;a href="https://un5qex02wb5tevr.julianrbryant.com/project/amaranth-yosys/"&gt;portable Yosys&lt;/a&gt; built-in, which works
beautifully. We’ll use it, but we’ll build it separately, too: for cases when we
want to make our own changes, or use step-through debugging to understand what’s
happening. It’s also necessary for formal verification.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/YosysHQ/nextpnr"&gt;nextpnr&lt;/a&gt; and &lt;a href="https://github.com/YosysHQ/icestorm"&gt;Project IceStorm&lt;/a&gt; are for targeting the Lattice &lt;a href="https://un5qgjbzw9dxcq3ecfxberhh.julianrbryant.com/wiki/ICE_(FPGA)#iCE40_(40_nm)"&gt;iCE40&lt;/a&gt; family
of FPGAs, which is known for its relative accessibility. I’ve been learning with
an &lt;a href="https://uhm6m5k461dwrwp9z81g.julianrbryant.com/products/icebreaker"&gt;iCEBreaker&lt;/a&gt; (&lt;a href="https://un5gmtkzgjwww5d6x3ceax70k0.julianrbryant.com/1bitsquared/icebreaker-fpga"&gt;see also&lt;/a&gt;), which is built around
the iCE40UP5k FPGA, and have found this to be true.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/YosysHQ/sby"&gt;SymbiYosys&lt;/a&gt; and &lt;a href="https://github.com/Z3Prover/z3"&gt;Z3&lt;/a&gt; are for &lt;a href="https://un5qgjbzw9dxcq3ecfxberhh.julianrbryant.com/wiki/Formal_verification"&gt;formal verification&lt;/a&gt;. I promise it’s good.&lt;/p&gt;
&lt;p&gt;Instructions are verified for Linux &lt;code&gt;x86_64&lt;/code&gt; and macOS &lt;code&gt;arm64&lt;/code&gt;. I intended to
cover Windows, too, but over four months found the experience
inconsistent&lt;sup class="footnote-ref"&gt;&lt;a href="#fn-windows" id="fnref-windows" data-footnote-ref=""&gt;2&lt;/a&gt;&lt;/sup&gt; enough that it was easier to use WSL 2&lt;sup class="footnote-ref"&gt;&lt;a href="#fn-wsl" id="fnref-wsl" data-footnote-ref=""&gt;3&lt;/a&gt;&lt;/sup&gt;. On Linux
and WSL, I’ve used Debian.&lt;/p&gt;
&lt;p&gt;I assume Linux users can install packages using the distribution package
manager, and macOS users using &lt;a href="https://un5h21kzgjquy.julianrbryant.com/"&gt;Homebrew&lt;/a&gt;. I’m going to avoid installing almost
anything globally, however, that wouldn’t already get installed by your package
manager as a matter of course, especially when there’s reasons you might need
multiple versions around.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="git"&gt;
&lt;h2&gt;Git &lt;a href="#git" aria-hidden="true" title="Permalink to section" class="anchor"&gt;🔗&lt;/a&gt; &lt;a href="#scope" aria-hidden="true" title="Back to scope" class="anchor"&gt;↩&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;a href="https://un5q027jw2wt1a8.julianrbryant.com/"&gt;Git&lt;/a&gt; website’s &lt;a href="https://un5q027jw2wt1a8.julianrbryant.com/downloads"&gt;Downloads&lt;/a&gt; page has instructions for
acquiring it through your relevant package manager.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="python-3"&gt;
&lt;h2&gt;Python 3 &lt;a href="#python-3" aria-hidden="true" title="Permalink to section" class="anchor"&gt;🔗&lt;/a&gt; &lt;a href="#scope" aria-hidden="true" title="Back to scope" class="anchor"&gt;↩&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;That was easy. Now the opinions start.&lt;/p&gt;
&lt;p&gt;Install the &lt;a href="https://un5pafrj4v491a8.julianrbryant.com/"&gt;&lt;code&gt;asdf&lt;/code&gt; Multiple Version Runtime Manager&lt;/a&gt;. The &lt;a href="https://un5pafrj4v491a8.julianrbryant.com/guide/getting-started.html"&gt;Getting
Started&lt;/a&gt; page has commands for dependency installation
through package manager. Use the official &lt;code&gt;git&lt;/code&gt; method to download &lt;code&gt;asdf&lt;/code&gt;
itself, and then follow the instructions for your shell.&lt;/p&gt;
&lt;p&gt;Now install the Python &lt;code&gt;asdf&lt;/code&gt; plugin, and install the latest stable version of
Python:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-console?prompt=$"&gt;&lt;span class="gp"&gt;~ $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;asdf plugin add python
&lt;span class="go"&gt;initializing plugin repository...Cloning into '/home/charlotte/.asdf/repository'...
remote: Enumerating objects: 5273, done.
remote: Counting objects: 100% (481/481), done.
remote: Compressing objects: 100% (88/88), done.
remote: Total 5273 (delta 419), reused 445 (delta 393), pack-reused 4792
Receiving objects: 100% (5273/5273), 1.21 MiB | 29.47 MiB/s, done.
Resolving deltas: 100% (2849/2849), done.
&lt;/span&gt;&lt;span class="gp"&gt;~ $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;asdf latest python
&lt;span class="go"&gt;3.11.4
&lt;/span&gt;&lt;span class="gp"&gt;~ $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;asdf &lt;span class="nb"&gt;install &lt;/span&gt;python 3.11.4
&lt;span class="go"&gt;python-build 3.11.4 /home/charlotte/.asdf/installs/python/3.11.4
Downloading Python-3.11.4.tar.xz...
-&amp;gt; https://un5gmtkzgjcywhd1hkae4.julianrbryant.com/ftp/python/3.11.4/Python-3.11.4.tar.xz
Installing Python-3.11.4...
Installed Python-3.11.4 to /home/charlotte/.asdf/installs/python/3.11.4
&lt;/span&gt;&lt;span class="gp"&gt;~ $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;asdf global python 3.11.4
&lt;span class="gp"&gt;~ $&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(You might get some warnings about extensions not being compiled. That’s OK.
There’s also 3.12.0b3 available at time of writing, if you don’t mind a beta.)&lt;/p&gt;
&lt;p&gt;The last command makes it the default Python for our user. &lt;code&gt;asdf&lt;/code&gt; puts some
shims in our &lt;code&gt;PATH&lt;/code&gt; which use a combination of our configured defaults
(&lt;code&gt;global&lt;/code&gt;), our current path (&lt;code&gt;local&lt;/code&gt;), and environment variables (&lt;code&gt;shell&lt;/code&gt;) to
select the desired version:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-console?prompt=$,&gt;&gt;&gt;"&gt;&lt;span class="gp"&gt;~ $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;which python
&lt;span class="go"&gt;/home/charlotte/.asdf/shims/python
&lt;/span&gt;&lt;span class="gp"&gt;~ $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;asdf current python
&lt;span class="go"&gt;python          3.11.4          /home/charlotte/.tool-versions
&lt;/span&gt;&lt;span class="gp"&gt;~ $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;asdf where python
&lt;span class="go"&gt;/home/charlotte/.asdf/installs/python/3.11.4
&lt;/span&gt;&lt;span class="gp"&gt;~ $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;asdf which python
&lt;span class="go"&gt;/home/charlotte/.asdf/installs/python/3.11.4/bin/python
&lt;/span&gt;&lt;span class="gp"&gt;~ $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;python
&lt;span class="go"&gt;Python 3.11.4 (main, Jun 26 2023, 16:06:57) [GCC 10.2.1 20210110] on linux
Type "help", "copyright", "credits" or "license" for more information.
&lt;/span&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/section&gt;
&lt;section id="venv"&gt;
&lt;h3&gt;venv &lt;a href="#venv" aria-hidden="true" title="Permalink to section" class="anchor"&gt;🔗&lt;/a&gt; &lt;a href="#scope" aria-hidden="true" title="Back to scope" class="anchor"&gt;↩&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The last thing we want to do is actually a per-project step. We’re about to
install Amaranth, which is a Python dependency, and so we want to make sure
we’re installing Python dependencies in a separate &lt;a href="https://un5n6892w35r21xfzp8f6wr.julianrbryant.com/3/library/venv.html"&gt;virtual environment&lt;/a&gt; per
project, that they don’t interfere or conflict with each other.&lt;/p&gt;
&lt;p&gt;In your project directory, create a new virtual environment called &lt;code&gt;venv&lt;/code&gt;, and
then activate it:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-console"&gt;&lt;span class="gp"&gt;prj $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; venv venv
&lt;span class="gp"&gt;prj $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;source &lt;/span&gt;venv/bin/activate
&lt;span class="gp"&gt;(venv) prj $&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(Note there are a few different &lt;code&gt;activate&lt;/code&gt; variants in the &lt;code&gt;bin&lt;/code&gt; directory for
different shells.)&lt;/p&gt;
&lt;p&gt;Add &lt;code&gt;venv&lt;/code&gt; to your &lt;code&gt;.gitignore&lt;/code&gt; or similar.&lt;/p&gt;
&lt;p&gt;It’s important to remember to activate the virtual environment before running
Python or installing dependencies with &lt;code&gt;pip&lt;/code&gt;. Many IDEs will automatically
activate (or prompt to activate) virtual environments when they’re detected in
the root of a project. Similarly, some shells can be configured to do similar.&lt;/p&gt;
&lt;p&gt;Note that the Python instance used by the virtual environment is tied to the
specific version we had chosen through &lt;code&gt;asdf&lt;/code&gt;, and not the shim:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-console"&gt;&lt;span class="gp"&gt;(venv) prj $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;readlink &lt;/span&gt;venv/bin/python
&lt;span class="go"&gt;/home/charlotte/.asdf/installs/python/3.11.4/bin/python
&lt;/span&gt;&lt;span class="gp"&gt;(venv) prj $&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We’re ready to install Python dependencies.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="amaranth"&gt;
&lt;h2&gt;Amaranth &lt;a href="#amaranth" aria-hidden="true" title="Permalink to section" class="anchor"&gt;🔗&lt;/a&gt; &lt;a href="#scope" aria-hidden="true" title="Back to scope" class="anchor"&gt;↩&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Firstly, note Amaranth’s own &lt;a href="https://un5mz2nwqavq3qfahkae4.julianrbryant.com/docs/amaranth/latest/install.html"&gt;installation instructions&lt;/a&gt;. We’ll follow along, and deviate from them somewhat.&lt;/p&gt;
&lt;p&gt;Install GTKWave from your package manager. We’ll come back to Yosys.&lt;/p&gt;
&lt;p&gt;Verify we do in fact have the latest &lt;code&gt;pip&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-console"&gt;&lt;span class="gp"&gt;(venv) prj $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--upgrade&lt;/span&gt; pip
&lt;span class="go"&gt;Requirement already satisfied: pip in ./venv/lib/python3.11/site-packages (23.1.2)
&lt;/span&gt;&lt;span class="gp"&gt;(venv) prj $&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We do not pass &lt;code&gt;--user&lt;/code&gt; to &lt;code&gt;pip&lt;/code&gt;—it is rejected in a virtual environment, for
&lt;code&gt;--user&lt;/code&gt; implies writing to your home directory, which would escape the virtual
environment.&lt;/p&gt;
&lt;p&gt;We’re going to skip the latest release and go straight to an editable
development snapshot. You may want to clone it within your project directory,
perhaps as a Git submodule, or along-side. I’m going with along-side.&lt;/p&gt;
&lt;p&gt;Clone Amaranth and install it in editable mode, with the built-in Yosys:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-console"&gt;&lt;span class="gp"&gt;(venv) prj $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ..
&lt;span class="gp"&gt;(venv) ~ $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;git clone https://github.com/amaranth-lang/amaranth
&lt;span class="go"&gt;Cloning into 'amaranth'...
remote: Enumerating objects: 8651, done.
remote: Counting objects: 100% (272/272), done.
remote: Compressing objects: 100% (95/95), done.
remote: Total 8651 (delta 170), reused 227 (delta 162), pack-reused 8379
Receiving objects: 100% (8651/8651), 1.71 MiB | 29.14 MiB/s, done.
Resolving deltas: 100% (6474/6474), done.
&lt;/span&gt;&lt;span class="gp"&gt;(venv) ~ $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;amaranth
&lt;span class="gp"&gt;(venv) amaranth $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--editable&lt;/span&gt; .[builtin-yosys]
&lt;span class="go"&gt;Obtaining file:///home/charlotte/amaranth
  Installing build dependencies ... done
  Checking if build backend supports build_editable ... done
  Getting requirements to build editable ... done
  Preparing editable metadata (pyproject.toml) ... done

[... lots of output ...]

Successfully built amaranth
Installing collected packages: wasmtime, pyvcd, MarkupSafe, Jinja2, amaranth-yosys, amaranth
Successfully installed Jinja2-3.1.2 MarkupSafe-2.1.3 amaranth-0.4.dev134+g99417d6 amaranth-yosys-0.25.0.0.post72 pyvcd-0.4.0 wasmtime-9.0.0
&lt;/span&gt;&lt;span class="gp"&gt;(venv) amaranth $&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that the virtual environment remained active even as we left the directory
we created it in. This is desirable: it means the editable snapshot was
installed in our virtual environment.&lt;/p&gt;
&lt;p&gt;We’ll install the &lt;a href="https://github.com/amaranth-lang/amaranth-boards"&gt;board definitions&lt;/a&gt; now, too. Clone the
repository and install it the same way, except without the &lt;code&gt;[builtin-yosys]&lt;/code&gt;
option:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-console"&gt;&lt;span class="gp"&gt;(venv) amaranth $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ..
&lt;span class="gp"&gt;(venv) ~ $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;git clone https://github.com/amaranth-lang/amaranth-boards
&lt;span class="go"&gt;Cloning into 'amaranth-boards'...
remote: Enumerating objects: 1353, done.
remote: Counting objects: 100% (532/532), done.
remote: Compressing objects: 100% (136/136), done.
remote: Total 1353 (delta 426), reused 405 (delta 396), pack-reused 821
Receiving objects: 100% (1353/1353), 307.90 KiB | 17.11 MiB/s, done.
Resolving deltas: 100% (943/943), done.
&lt;/span&gt;&lt;span class="gp"&gt;(venv) ~ $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;amaranth-boards/
&lt;span class="gp"&gt;(venv) amaranth-boards $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--editable&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="go"&gt;Obtaining file:///home/charlotte/amaranth-boards
  Installing build dependencies ... done
  Checking if build backend supports build_editable ... done
  Getting requirements to build editable ... done
  Preparing editable metadata (pyproject.toml) ... done

[... lots of output ...]

Successfully built amaranth-boards
Installing collected packages: amaranth-boards
Successfully installed amaranth-boards-0.1.dev228+g54e6ac4
&lt;/span&gt;&lt;span class="gp"&gt;(venv) amaranth-boards $&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We’re ready. We can verify the installations by using a Python shell in the
virtual environment:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-console?prompt=$,&gt;&gt;&gt;"&gt;&lt;span class="gp"&gt;(venv) prj $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;python
&lt;span class="go"&gt;Python 3.11.4 (main, Jun 26 2023, 16:06:57) [GCC 10.2.1 20210110] on linux
Type "help", "copyright", "credits" or "license" for more information.
&lt;/span&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;from amaranth import &lt;span class="k"&gt;*&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Signal
&lt;span class="go"&gt;&amp;lt;class 'amaranth.hdl.ast.Signal'&amp;gt;
&lt;/span&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;from amaranth_boards.icebreaker import &lt;span class="k"&gt;*&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ICEBreakerPlatform
&lt;span class="go"&gt;&amp;lt;class 'amaranth_boards.icebreaker.ICEBreakerPlatform'&amp;gt;
&lt;/span&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/section&gt;
&lt;section id="yosys"&gt;
&lt;h2&gt;Yosys &lt;a href="#yosys" aria-hidden="true" title="Permalink to section" class="anchor"&gt;🔗&lt;/a&gt; &lt;a href="#scope" aria-hidden="true" title="Back to scope" class="anchor"&gt;↩&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Clone the &lt;a href="https://github.com/yosyshq/yosys"&gt;Yosys repo&lt;/a&gt; and read its README. &lt;a href="https://github.com/yosyshq/yosys#building-from-source"&gt;§ Building from Source&lt;/a&gt; tells you
which packages from the package manager it needs.&lt;/p&gt;
&lt;p&gt;By default, Yosys will install into &lt;code&gt;/usr/local&lt;/code&gt;, but we’ll override it to use
&lt;code&gt;~/.local&lt;/code&gt; instead. We do this by setting the &lt;code&gt;PREFIX&lt;/code&gt; variable, and
importantly, we need it set for the build step too, not only install. Otherwise,
the &lt;code&gt;yosys-config&lt;/code&gt; helper that gets installed will report the wrong values.&lt;/p&gt;
&lt;p&gt;Yosys’s Makefile will include &lt;code&gt;Makefile.conf&lt;/code&gt; if it exists; we’ll put it in
there so we can’t forget, and don’t have to stash Makefile changes when we pull
the latest. Then we build in parallel and install:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-console?prompt=%20yosys%20$"&gt;&lt;span class="gp"&gt;(venv) yosys $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'PREFIX = $(HOME)/.local'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Makefile.conf
&lt;span class="gp"&gt;(venv) yosys $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;make &lt;span class="nt"&gt;-j8&lt;/span&gt;
&lt;span class="go"&gt;[Makefile.conf] PREFIX = $(HOME)/.local
[  0%] Building kernel/version_2310a0ea9.cc
[  0%] Building kernel/driver.o
[  0%] Building techlibs/common/simlib_help.inc
[  0%] Building techlibs/common/simcells_help.inc
[  1%] Building kernel/rtlil.o

[... lots of output ...]

[ 94%] ABC: `` Compiling: /src/bdd/llb/llb4Nonlin.c
[ 94%] ABC: `` Compiling: /src/bdd/llb/llb4Sweep.c
[ 94%] ABC: `` Building binary: abc-1de4eaf
[100%] Building yosys-abc

  Build successful.

&lt;/span&gt;&lt;span class="gp"&gt;(venv) yosys $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;make &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;span class="go"&gt;[Makefile.conf] PREFIX = $(HOME)/.local
mkdir -p /home/charlotte/.local/bin
cp yosys yosys-config yosys-abc yosys-filterlib yosys-smtbmc yosys-witness /home/charlotte/.local/bin
strip -S /home/charlotte/.local/bin/yosys
strip /home/charlotte/.local/bin/yosys-abc
strip /home/charlotte/.local/bin/yosys-filterlib
mkdir -p /home/charlotte/.local/share/yosys
cp -r share/. /home/charlotte/.local/share/yosys/.
&lt;/span&gt;&lt;span class="gp"&gt;(venv) yosys $&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You may need to add &lt;code&gt;~/.local/bin&lt;/code&gt; to your &lt;code&gt;PATH&lt;/code&gt;. Test the installed binary.
Check the &lt;code&gt;yosys-config&lt;/code&gt; output:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-console"&gt;&lt;span class="gp"&gt;(venv) yosys $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;yosys-config &lt;span class="nt"&gt;--datdir&lt;/span&gt;
&lt;span class="go"&gt;/home/charlotte/.local/share/yosys
&lt;/span&gt;&lt;span class="gp"&gt;(venv) yosys $&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/section&gt;
&lt;section id="project-icestorm"&gt;
&lt;h2&gt;Project IceStorm &lt;a href="#project-icestorm" aria-hidden="true" title="Permalink to section" class="anchor"&gt;🔗&lt;/a&gt; &lt;a href="#scope" aria-hidden="true" title="Back to scope" class="anchor"&gt;↩&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Before &lt;a href="https://github.com/YosysHQ/nextpnr"&gt;nextpnr&lt;/a&gt;, we need the technology-specific support. That’s this step.&lt;/p&gt;
&lt;p&gt;Clone &lt;a href="https://github.com/YosysHQ/icestorm"&gt;Project IceStorm&lt;/a&gt;. There’s no &lt;code&gt;Makefile.conf&lt;/code&gt; here, so edit the first
line of &lt;code&gt;config.mk&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-diff"&gt;&lt;span class="gd"&gt;-PREFIX ?= /usr/local
&lt;/span&gt;&lt;span class="gi"&gt;+PREFIX = $(HOME)/.local&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Install the libftdi development package; it’s &lt;code&gt;libftdi-dev&lt;/code&gt; on Debian and
&lt;code&gt;libftdi&lt;/code&gt; in Homebrew.&lt;/p&gt;
&lt;p&gt;Now compile and install Project IceStorm. I’ve avoided compiling in parallel as
its build script sometimes gets ahead of itself:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-console?prompt=icestorm%20$"&gt;&lt;span class="gp"&gt;(venv) icestorm $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;make
&lt;span class="go"&gt;make -C icebox all
make[1]: Entering directory '/home/charlotte/icestorm/icebox'
python3 icebox_chipdb.py -3 &amp;gt; chipdb-384.new
mv chipdb-384.new chipdb-384.txt

[... lots of output ...]

cc -MD -MP -O2  -Wall -std=c99 -I/home/charlotte/.local/include    -c -o mpsse.o mpsse.c
cc -o iceprog  iceprog.o mpsse.o -lm -lstdc++ -lftdi
make[1]: Leaving directory '/home/charlotte/icestorm/iceprog'
&lt;/span&gt;&lt;span class="gp"&gt;(venv) icestorm $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;make &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;span class="go"&gt;for dir in icebox icepack icemulti icepll icebram icetime iceprog; do \
        make -C $dir install || exit; \
done
make[1]: Entering directory '/home/charlotte/icestorm/icebox'
mkdir -p /home/charlotte/.local/share/icebox

[... lots of output ...]

mkdir -p /home/charlotte/.local/bin
cp iceprog /home/charlotte/.local/bin/iceprog
make[1]: Leaving directory '/home/charlotte/icestorm/iceprog'
&lt;/span&gt;&lt;span class="gp"&gt;(venv) icestorm $&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you have an iCEBreaker, at this stage you can try using &lt;code&gt;iceprog&lt;/code&gt; to say hi:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-console"&gt;&lt;span class="gp"&gt;(venv) icestorm $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;iceprog &lt;span class="nt"&gt;-t&lt;/span&gt;
&lt;span class="go"&gt;init..
cdone: high
reset..
cdone: low
flash ID: 0xEF 0x40 0x18 0x00
cdone: high
Bye.
&lt;/span&gt;&lt;span class="gp"&gt;(venv) icestorm $&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/section&gt;
&lt;section id="troubleshooting"&gt;
&lt;h3&gt;Troubleshooting &lt;a href="#troubleshooting" aria-hidden="true" title="Permalink to section" class="anchor"&gt;🔗&lt;/a&gt; &lt;a href="#scope" aria-hidden="true" title="Back to scope" class="anchor"&gt;↩&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The following error indicates the device wasn’t found by &lt;code&gt;iceprog&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;init..
Can't find iCE FTDI USB device (vendor_id 0x0403, device_id 0x6010 or 0x6014).
ABORT.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;macOS users, check System Information → USB. If you don’t see the iCEBreaker
listed, check your connections and consider trying a different USB cable,
adaptor or hub, as appropriate.&lt;/p&gt;
&lt;p&gt;Linux users, check &lt;code&gt;lsusb&lt;/code&gt;. If you can see something with ID &lt;code&gt;0403:6010&lt;/code&gt;, that’s
good. If it identifies itself as an iCEBreaker, even better. You may need a
&lt;a href="https://un5mvxzy68pm0.julianrbryant.com/article/18/11/udev"&gt;udev&lt;/a&gt; rule to ensure the device node is writable by your user.&lt;/p&gt;
&lt;p&gt;Create the file &lt;code&gt;/etc/udev/rules.d/53-lattice-ftdi.rules&lt;/code&gt; with the following
content:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6010", MODE="0660", GROUP="plugdev", TAG+="uaccess"
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will make any device with ID &lt;code&gt;0403:6010&lt;/code&gt; writable by the group &lt;code&gt;plugdev&lt;/code&gt;.
Check the output of the &lt;code&gt;id&lt;/code&gt; command to verify your user groups:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-console"&gt;&lt;span class="gp"&gt;(venv) icestorm $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="nt"&gt;-Gn&lt;/span&gt;
&lt;span class="go"&gt;charlotte adm sudo plugdev
&lt;/span&gt;&lt;span class="gp"&gt;(venv) icestorm $&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If &lt;code&gt;plugdev&lt;/code&gt; is listed somewhere, you’re good. Otherwise, add yourself to the
group. (e.g. &lt;code&gt;sudo adduser $(whoami) plugdev&lt;/code&gt;) After this, unplug and replug for
the new rule to take effect.&lt;/p&gt;
&lt;p&gt;WSL 2 users (or recalcitrant Windows users) should also consult the
footnote&lt;sup class="footnote-ref"&gt;&lt;a href="#fn-wslice" id="fnref-wslice" data-footnote-ref=""&gt;4&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="nextpnr"&gt;
&lt;h2&gt;nextpnr &lt;a href="#nextpnr" aria-hidden="true" title="Permalink to section" class="anchor"&gt;🔗&lt;/a&gt; &lt;a href="#scope" aria-hidden="true" title="Back to scope" class="anchor"&gt;↩&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Ensure Project IceStorm (&lt;a href="#project-icestorm"&gt;↴&lt;/a&gt;) is installed first.&lt;/p&gt;
&lt;p&gt;Fetch &lt;a href="https://github.com/YosysHQ/nextpnr"&gt;nextpnr&lt;/a&gt; and install the appropriate &lt;a href="https://github.com/YosysHQ/nextpnr#prerequisites"&gt;§ Prerequisites&lt;/a&gt;. Then, check out
the specific instructions for &lt;a href="https://github.com/YosysHQ/nextpnr#nextpnr-ice40"&gt;nextpnr-ice40&lt;/a&gt;. We’ll need to adapt them
slightly.&lt;/p&gt;
&lt;p&gt;We specify the iCE40 arch, the install prefix, the install prefix for Project
IceStorm, the root directory for our active Python installation, and finally, a
&lt;a href="https://un5n6095tepe2p27xu8b698.julianrbryant.com/blog/2021/08/04/understanding-rpath-with-cmake/"&gt;runtime search path&lt;/a&gt; to add to the final binary. This is because nextpnr
will link against our Python install, but our Python install’s shared libraries
aren’t on the &lt;a href="https://un5upby4gkm55apnw0240mqkk0.julianrbryant.com/a/22999/577154"&gt;system search path&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-console?prompt=nextpnr%20$,%20%20%20%20"&gt;&lt;span class="gp"&gt;(venv) nextpnr $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;cmake &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-DARCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ice40 &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="gp"&gt;    &lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="nt"&gt;-DCMAKE_INSTALL_PREFIX&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;/.local &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="gp"&gt;    &lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="nt"&gt;-DICESTORM_INSTALL_PREFIX&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;/.local &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="gp"&gt;    &lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="nt"&gt;-DPython3_ROOT_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;asdf where python&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="gp"&gt;    &lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="nt"&gt;-DCMAKE_INSTALL_RPATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;asdf where python&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;/lib
&lt;span class="go"&gt;-- Building with IPO
-- Found Python3: /home/charlotte/.asdf/installs/python/3.11.4/bin/python3 (found suitable version "3.11.4", minimum required is "3.5") found components: Interpreter
-- Found Python3: /home/charlotte/.asdf/installs/python/3.11.4/include/python3.11 (found suitable version "3.11.4", minimum required is "3.5") found components: Development Development.Module Development.Embed
-- Found Boost: /usr/include (found version "1.74.0") found components: filesystem program_options iostreams system thread regex chrono date_time atomic
-- Found Boost: /usr/include (found version "1.74.0") found components: program_options filesystem system
-- Configuring architecture: ice40
-- Enabled iCE40 devices: 384;1k;5k;u4k;8k
-- Found Python3: /home/charlotte/.asdf/installs/python/3.11.4/bin/python3 (found suitable version "3.11.4", minimum required is "3.5") found components: Interpreter
-- IceStorm install prefix: /home/charlotte/.local
-- icebox data directory: /home/charlotte/.local/share/icebox
-- Using iCE40 chipdb: /home/charlotte/nextpnr/ice40/chipdb
-- Configuring architecture: ecp5
-- Enabled ECP5 devices: 25k;45k;85k
-- Trellis install prefix: /home/charlotte/.local
-- Trellis library directory: /usr/local/lib/trellis
-- Trellis data directory: /home/charlotte/.local/share/trellis
-- Using ECP5 chipdb: /home/charlotte/nextpnr/ecp5/chipdb
-- Configuring done
-- Generating done
-- Build files have been written to: /home/charlotte/nextpnr
&lt;/span&gt;&lt;span class="gp"&gt;(venv) nextpnr $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;make &lt;span class="nt"&gt;-j8&lt;/span&gt;
&lt;span class="go"&gt;[  2%] Generating chipdb/chipdb-384.bba
[  2%] Building CXX object bba/CMakeFiles/bbasm.dir/main.cc.o
[  4%] Generating chipdb/chipdb-1k.bba
[  5%] Linking CXX executable bbasm

[... lots of output ...]

[ 97%] Building CXX object CMakeFiles/nextpnr-ice40.dir/ice40/pack.cc.o
[ 98%] Building CXX object CMakeFiles/nextpnr-ice40.dir/ice40/pcf.cc.o
[100%] Linking CXX executable nextpnr-ice40
[100%] Built target nextpnr-ice40
&lt;/span&gt;&lt;span class="gp"&gt;(venv) nextpnr $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;make &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;span class="go"&gt;[  7%] Built target chipdb-ice40-bbas
[ 10%] Built target bbasm
[ 17%] Built target chipdb-ice40-bins
[ 32%] Built target chipdb-ice40
[100%] Built target nextpnr-ice40
Install the project...
-- Install configuration: "Release"
-- Installing: /home/charlotte/.local/bin/nextpnr-ice40
-- Set runtime path of "/home/charlotte/.local/bin/nextpnr-ice40" to "/home/charlotte/.asdf/installs/python/3.11.4/lib"
&lt;/span&gt;&lt;span class="gp"&gt;(venv) nextpnr $&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Test the installed binary to make sure it works.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-console"&gt;&lt;span class="gp"&gt;(venv) nextpnr $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nextpnr-ice40
&lt;span class="go"&gt;"nextpnr-ice40" -- Next Generation Place and Route (Version nextpnr-0.6-29-g54b20457)

General options:
  -h [ --help ]                         show help
  -v [ --verbose ]                      verbose output
  -q [ --quiet ]                        quiet mode, only errors and warnings
                                        displayed
  --Werror                              Turn warnings into errors
  -l [ --log ] arg                      log file, all log messages are written
                                        to this file regardless of -q

[... lots of output ...]

  --opt-timing                          run post-placement timing optimisation
                                        pass (experimental)
  --tmfuzz                              run path delay estimate fuzzer
  --pcf-allow-unconstrained             don't require PCF to constrain all IO

&lt;/span&gt;&lt;span class="gp"&gt;(venv) nextpnr $&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At this point our Amaranth can now use our installed tooling to program the
iCEBreaker. The board definitions we installed earlier can be executed directly
to program a test blink gateware—doing this exercises the full toolchain.
Verify:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-console?prompt=nextpnr%20$"&gt;&lt;span class="gp"&gt;(venv) nextpnr $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; amaranth_boards.icebreaker
&lt;span class="go"&gt;init..
cdone: high
reset..
cdone: low
flash ID: 0xEF 0x40 0x18 0x00
file size: 104090
erase 64kB sector at 0x000000..
erase 64kB sector at 0x010000..
programming..
done.
reading..
VERIFY OK
cdone: high
Bye.
&lt;/span&gt;&lt;span class="gp"&gt;(venv) nextpnr $&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/section&gt;
&lt;section id="symbiyosys"&gt;
&lt;h2&gt;SymbiYosys &lt;a href="#symbiyosys" aria-hidden="true" title="Permalink to section" class="anchor"&gt;🔗&lt;/a&gt; &lt;a href="#scope" aria-hidden="true" title="Back to scope" class="anchor"&gt;↩&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://un5qgjbzw9dxcq3ecfxberhh.julianrbryant.com/wiki/Formal_verification"&gt;Formal verification&lt;/a&gt; can be orchestrated with SymbiYosys. To get started with
formal verification and Amaranth, have a look at &lt;a href="https://github.com/RobertBaruch/amaranth-exercises"&gt;Robert Baruch’s graded
exercises for Amaranth HDL&lt;/a&gt;, which start with formal methods
from the very first exercise. They use the tools we install here.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/YosysHQ/sby"&gt;SymbiYosys&lt;/a&gt; is a relatively simple frontend, so fetch the repo and install. It
also has a Python dependency, &lt;code&gt;click&lt;/code&gt;. Check that &lt;code&gt;sby -h&lt;/code&gt; doesn’t give an
error:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-console?prompt=sby%20$"&gt;&lt;span class="gp"&gt;(venv) sby $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;make &lt;span class="nv"&gt;PREFIX&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;/.local &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;span class="go"&gt;mkdir -p /home/charlotte/.local/bin
mkdir -p /home/charlotte/.local/share/yosys/python3
cp sbysrc/sby_*.py /home/charlotte/.local/share/yosys/python3/
sed -e 's|##yosys-program-prefix##|"''"|' &amp;lt; sbysrc/sby_core.py &amp;gt; /home/charlotte/.local/share/yosys/python3/sby_core.py
sed 's|##yosys-sys-path##|sys.path += [os.path.dirname(__file__) + p for p in ["/share/python3", "/../share/yosys/python3"]]|;' &amp;lt; sbysrc/sby.py &amp;gt; /home/charlotte/.local/bin/sby
chmod +x /home/charlotte/.local/bin/sby
&lt;/span&gt;&lt;span class="gp"&gt;(venv) sby $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;click
&lt;span class="go"&gt;Collecting click
  Using cached click-8.1.3-py3-none-any.whl (96 kB)
Installing collected packages: click
Successfully installed click-8.1.3
&lt;/span&gt;&lt;span class="gp"&gt;(venv) sby $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;sby &lt;span class="nt"&gt;-h&lt;/span&gt;
&lt;span class="go"&gt;usage: sby [options] [&amp;lt;jobname&amp;gt;.sby [tasknames] | &amp;lt;dirname&amp;gt;]

positional arguments:
  &amp;lt;jobname&amp;gt;.sby | &amp;lt;dirname&amp;gt;

[... lots of output ...]

  --init-config-file INIT_CONFIG_FILE
                        create a default .sby config file
&lt;/span&gt;&lt;span class="gp"&gt;(venv) sby $&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Check &lt;code&gt;sby -h&lt;/code&gt; doesn’t give an error.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="z3"&gt;
&lt;h2&gt;Z3 &lt;a href="#z3" aria-hidden="true" title="Permalink to section" class="anchor"&gt;🔗&lt;/a&gt; &lt;a href="#scope" aria-hidden="true" title="Back to scope" class="anchor"&gt;↩&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/Z3Prover/z3"&gt;Z3&lt;/a&gt; is a &lt;a href="https://un5qgjbzw9dxcq3ecfxberhh.julianrbryant.com/wiki/Automated_theorem_proving"&gt;theorem prover&lt;/a&gt; — it does the heavy lifting of formal verification.
Clone the repo; we’re going to follow the &lt;a href="https://github.com/Z3Prover/z3/blob/master/README-CMake.md"&gt;CMake instructions&lt;/a&gt;.  The defaults
are all good, except for the install prefix:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-console"&gt;&lt;span class="gp"&gt;(venv) z3 $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;build
&lt;span class="gp"&gt;(venv) z3 $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;build
&lt;span class="gp"&gt;(venv) build $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;cmake .. &lt;span class="nt"&gt;-DCMAKE_INSTALL_PREFIX&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;/.local
&lt;span class="go"&gt;-- The CXX compiler identification is GNU 12.2.0
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Z3 version 4.12.3.0

[... lots of output ...]

-- Configuring done
-- Generating done
-- Build files have been written to: /home/charlotte/z3/build
&lt;/span&gt;&lt;span class="gp"&gt;(venv) build $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;make &lt;span class="nt"&gt;-j8&lt;/span&gt;
&lt;span class="go"&gt;[  0%] Building CXX object src/util/CMakeFiles/util.dir/approx_set.cpp.o
[  0%] Building CXX object src/util/CMakeFiles/util.dir/approx_nat.cpp.o
[  0%] Building CXX object src/util/CMakeFiles/util.dir/debug.cpp.o

[... lots of output ...]

[ 98%] Linking CXX shared library ../libz3.so
[100%] Linking CXX executable ../../z3
[100%] Built target shell
[100%] Built target libz3
&lt;/span&gt;&lt;span class="gp"&gt;(venv) build $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;make &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;span class="go"&gt;[  6%] Built target util
[  8%] Built target params
[  9%] Built target polynomial
[  9%] Built target automata

[... lots of output ...]

-- Installing: /home/charlotte/.local/include/z3_spacer.h
-- Installing: /home/charlotte/.local/include/z3_version.h
-- Installing: /home/charlotte/.local/bin/z3
&lt;/span&gt;&lt;span class="gp"&gt;(venv) build $&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Done.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="overview"&gt;
&lt;h2&gt;Overview &lt;a href="#overview" aria-hidden="true" title="Permalink to section" class="anchor"&gt;🔗&lt;/a&gt; &lt;a href="#scope" aria-hidden="true" title="Back to scope" class="anchor"&gt;↩&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You’re now ready to write and deploy gateware, with a toolchain selected, built
and installed by yourself in a self-contained and repeatable way.&lt;/p&gt;
&lt;p&gt;There are many ways to pivot from here.&lt;/p&gt;
&lt;h5&gt;You need a different Python version.&lt;/h5&gt;
&lt;p&gt;&lt;code&gt;asdf&lt;/code&gt; and virtual environments make this easy.&lt;/p&gt;
&lt;h5&gt;You want to understand Amaranth better.&lt;/h5&gt;
&lt;p&gt;You can modify your editable install directly, adding print debugging.&lt;/p&gt;
&lt;h5&gt;You need to write pure Verilog.&lt;/h5&gt;
&lt;p&gt;You can drive Yosys yourself.&lt;/p&gt;
&lt;h5&gt;You want to understand decisions made by Yosys better.&lt;/h5&gt;
&lt;p&gt;You can step-through debug Yosys: run your Amaranth build once, then invoke
Yosys with your debugger of choice using the arguments from the generated
&lt;code&gt;build/build_top.sh&lt;/code&gt;.&lt;/p&gt;
&lt;h5&gt;You need to target a different family of boards.&lt;/h5&gt;
&lt;p&gt;nextpnr &lt;a href="https://github.com/YosysHQ/nextpnr#readme"&gt;supports&lt;/a&gt; a range of architectures.&lt;/p&gt;
&lt;h5&gt;You want to use a different solver with SymbiYosys.&lt;/h5&gt;
&lt;p&gt;If it’s &lt;a href="https://un5m2x0kp2pywmnmp7ubeuf2cjb9rd2tvcx0.julianrbryant.com/en/latest/install.html#recommended-components"&gt;supported&lt;/a&gt; and on your &lt;code&gt;PATH&lt;/code&gt;, it’ll work.&lt;/p&gt;
&lt;/section&gt;
&lt;section class="footnotes" data-footnotes="" id="footnotes"&gt;&lt;h2&gt;Footnotes &lt;a href="#footnotes" aria-hidden="true" title="Permalink to section" class="anchor"&gt;🔗&lt;/a&gt; &lt;a href="#scope" aria-hidden="true" title="Back to scope" class="anchor"&gt;↩&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li id="fn-baby"&gt;
&lt;p&gt;&lt;em&gt;baby’s first gateware&lt;/em&gt;, in fact. &lt;a href="#fnref-baby" class="footnote-backref" data-footnote-backref="" data-footnote-backref-idx="1" aria-label="Back to reference 1"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn-windows"&gt;
&lt;ul&gt;
&lt;li&gt;Lots of random things are a little bit broken.&lt;/li&gt;
&lt;li&gt;Building Yosys is certainly achievable but &lt;a href="https://github.com/YosysHQ/yosys/blob/2310a0ea9a61ed14d2769f01283a5a7590cbe558/guidelines/Windows"&gt;you simply don’t wanna&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Everything runs slower&lt;/em&gt;. Everything. Git runs slower. Python runs slower.
Batch scripts run slower. Yosys runs slower. &lt;code&gt;iceprog&lt;/code&gt; communicates
(much!) slower.&lt;/li&gt;
&lt;li&gt;Think you can fix some of this by using MSYS2 or Cygwin? Now you have two
problems.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There’s more that I’ve decided was better left forgotten. &lt;a href="#fnref-windows" class="footnote-backref" data-footnote-backref="" data-footnote-backref-idx="2" aria-label="Back to reference 2"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn-wsl"&gt;
&lt;p&gt;tl;dr: use your Linux user home directory, not your Windows user one; if
CMake takes forever during configure, check if your &lt;code&gt;PATH&lt;/code&gt; is full of
&lt;code&gt;/mnt/...&lt;/code&gt; — if it is, it’s probably searching your Windows partition very
slowly (disable &lt;a href="https://un5hru1qgj43w9rdtvyj8.julianrbryant.com/en-us/windows/wsl/wsl-config#interop-settings"&gt;&lt;code&gt;interop.appendWindowsPath&lt;/code&gt;&lt;/a&gt; or modify your &lt;code&gt;PATH&lt;/code&gt; just for
configure); when it comes time to flash your board, follow the guide to
&lt;a href="https://un5hru1qgj43w9rdtvyj8.julianrbryant.com/en-us/windows/wsl/connect-usb"&gt;Connect USB devices&lt;/a&gt; using &lt;a href="https://github.com/dorssel/usbipd-win"&gt;usbipd-win&lt;/a&gt;. Don’t mind the scary warning on
the guide: I didn’t have to recompile my kernel even on Windows 10. &lt;a href="#fnref-wsl" class="footnote-backref" data-footnote-backref="" data-footnote-backref-idx="3" aria-label="Back to reference 3"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn-wslice"&gt;
&lt;p&gt;Firstly, create (or edit) the file &lt;code&gt;/etc/wsl.conf&lt;/code&gt; and ensure you
have the following stanza (or know what you’re doing already):&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-ini"&gt;&lt;span class="nn"&gt;[boot]&lt;/span&gt;
&lt;span class="py"&gt;command&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"service udev restart"&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Run &lt;code&gt;sudo service udev restart&lt;/code&gt; to get udev going immediately. You can
&lt;code&gt;usbipd wsl detach -i 0403:6010&lt;/code&gt; and then &lt;code&gt;attach&lt;/code&gt; again instead of
physically messing around with cables.&lt;/p&gt;
&lt;p&gt;This likely only applies to Windows Steelman Enthusiasts, but you &lt;em&gt;may&lt;/em&gt; also
need to use &lt;a href="https://un5t1ktpu75vkapnw3wbeg08.julianrbryant.com/"&gt;Zadig&lt;/a&gt;. Use the WinUSB driver. Check “List All Devices” in the
options menu. You should see two entries that correspond to the iCEBreaker —
“Interface 0” and “Interface 1” — and they might identify themselves as the
iCEBreaker, or something less obvious (like “Dual RS232-HS”). Make sure you
use the same driver for both. When in doubt, unplug and replug. &lt;a href="#fnref-wslice" class="footnote-backref" data-footnote-backref="" data-footnote-backref-idx="4" aria-label="Back to reference 4"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content>
  </entry>
</feed>

