[doc] update SW build and Verilator docs to reference Bazel

Now that all meson build files have been removed in favor of Bazel, this
commit updates the SW build and Verilator build documentation on the
website. Note, the FPGA documentation will be updated in a follow on
commit.

Signed-off-by: Timothy Trippel <ttrippel@google.com>
diff --git a/doc/getting_started/_index.md b/doc/getting_started/_index.md
index ab4e599..c4d3cb0 100644
--- a/doc/getting_started/_index.md
+++ b/doc/getting_started/_index.md
@@ -128,7 +128,7 @@
 
 In order to run the software we built in the last step, we need to have some way to emulate an OpenTitan chip.
 There are a few different options depending on your equipment and use-case.
-Follow the guide that applies to you:
+Follow the guide(s) that applies to you:
 * **Option 1 (Verilator setup, recommended for new users):** [Verilator guide]({{< relref "setup_verilator.md" >}}), or
 * Option 2 (FPGA setup): [FPGA guide]({{< relref "setup_fpga.md" >}}), or
 * Option 3 (design verification setup): [DV guide]({{< relref "setup_dv.md" >}})
@@ -208,4 +208,3 @@
 * [OpenTitan Software]({{< relref "/sw" >}})
 * [Writing and Building Software for OTBN]({{< relref "otbn_sw.md" >}})
 * [Rust for Embedded C Programmers]({{< relref "rust_for_c.md" >}})
-
diff --git a/doc/getting_started/build_sw.md b/doc/getting_started/build_sw.md
index 8593f89..c88c050 100644
--- a/doc/getting_started/build_sw.md
+++ b/doc/getting_started/build_sw.md
@@ -1,92 +1,44 @@
 ---
-title: Building Software
+title: Building (and Testing) Software
 aliases:
     - /doc/ug/getting_started_build_sw
 ---
 
-_Before following this guide, make sure you've followed the [dependency installation instructions]({{< relref "getting_started" >}})._
+_Before following this guide, make sure you have read the_:
+* main [Getting Started]({{< relref "getting_started" >}}) instructions, and
+* [OpenTitan Software]({{< relref "sw/" >}}) documentation.
 
-## Building software with Meson
+All OpenTitan software is built with [Bazel](https://bazel.build/).
+Additionally, _most_ tests may be run with Bazel too.
 
-OpenTitan software is built using [Meson](https://mesonbuild.com).
-However, Meson is not an exact fit for a lot of things OpenTitan does (such as distinguishing between FPGA, ASIC, and simulations), so the setup is a little bit different.
-
-For example, the following commands build the `test_rom` and `hello_world` binaries for FPGA:
+## TL;DR
+To install the correct version of bazel, build, and run all on-host tests you can simply run:
 
 ```console
-# Configure the Meson environment.
-cd $REPO_TOP
-./meson_init.sh
-
-# Build the two targets we care about, specifically.
-ninja -C build-out sw/device/lib/testing/test_rom/test_rom_export_fpga_cw310
-ninja -C build-out sw/device/examples/hello_world/hello_world_export_fpga_cw310
-
-# Build *everything*, including targets for other devices.
-ninja -C build-out all
+$REPO_TOP/bazelisk.sh test //... --test_tag_filters=-cw310,-verilator --disk_cache=~/bazel_cache
 ```
 
-Note that specific targets are followed by the device they are built for.
-OpenTitan needs to link the same device executable for multiple devices, so each executable target is duplicated one for each device we support.
+## Installing Bazel
 
-In general, `clean` rules are unnecessary, and Meson will set up `ninja` such that it reruns `meson.build` files which have changed.
+There are two ways to install the correct verion of Bazel:
+1. **automatically**, using the `bazelisk.sh` script provided in the repo, or
+1. **manually**.
 
-Build intermediates will show up in `$REPO_TOP/build-out`, including unlinked object files and libraries, while completed executables are exported to `$REPO_TOP/build-bin`.
-As a rule, you should only ever need to refer to artifacts inside of `build-bin`; the exact structure of `build-out` is subject to change.
-Complete details of these semantics are documented in `util/build_consts.sh`.
+### Automatic Installation
 
-The locations of `build-{out,bin}` can be controled by setting the `$BUILD_ROOT` enviromnent variable, which defaults to `$REPO_TOP`.
-
-`./meson_init.sh` itself is idempotent, but this behavior can be changed with additional flags; see `./meson_init.sh` for more information.
-For this reason, most examples involving Meson will include a call to `./meson_init.sh`, but you will rarely need to run it more than once per checkout.
-
-Building an executable `foo` destined to run on the OpenTitan device `$DEVICE` will output the following files under `build-bin/sw/device`:
-* `foo_$DEVICE.elf`: the linked program, in ELF format.
-* `foo_$DEVICE.bin`: the linked program, as a plain binary with ELF debug information removed.
-* `foo_$DEVICE.dis`: the disassembled program with inline source code.
-* `foo_$DEVICE.vmem`: a Verilog memory file which can be read by `$readmemh()` in Verilog code.
-
-In general, this executable is built by the `foo_export_$DEVICE` target.
-For example, this builds the `pwrmgr_smoketest` test binary for DEVICE `sim_dv`:
-
-```console
-ninja -C build-out sw/device/tests/pwrmgr_smoketest_export_sim_dv
-```
-
-Building an executable destined to run on a host machine (i.e., under `sw/host`) will output a host excecutable under `build-bin/sw/host`, which can be run directly.
-
-### Troubleshooting the build system
-
-If you encounter an error running `./meson_init.sh` you could re-run using the `-f` flag which will erase any existing building directories to yield a clean build.
-This sledgehammer is only intended to be used as a last resort when the existing configuration is seriously broken:
-
-```console
-./meson_init.sh -f
-```
-
-If any `meson.build` files are changed the configuration can be regenerated by passing the `-r` flag to `./meson_init.sh`:
-
-```console
-./meson_init.sh -r
-```
-## Building Software with Bazel
-
-The Mask ROM and many other software targets are built with [Bazel](https://bazel.build/)
-
-_TLDR: You can run:_
-
-```console
-$REPO_TOP/bazelisk.sh test //... --test_tag_filters=-cw310 --disk_cache=~/bazel_cache
-```
-
-### Installing Bazel
-
-_Bazelisk and the script that installs it will work, but by installing Bazel you can get some quality of life features like tab completion._
-This section is optional and can be skipped by setting the alias:
+To simplify the installation of Bazel, and provide a means to seamlessly update the Bazel version we use in the future, we provide a shell script that acts as a wrapper for invocations of "`bazel ...`".
+To use it, you two options:
+1. use "`./bazelisk.sh ...`" instead of "`bazel ...`" to invoke of Bazel subcommands, or
+1. set the following alias (e.g., in your `.bashrc` file) to accomplish the same:
 ```console
 alias bazel="$REPO_TOP/bazelisk.sh"
 ```
 
+### Manual Installation
+
+This section is optional and can be skipped if you completed the instructions above in _Automatic Installation_.
+
+While the automatic installation is convenient, by installing Bazel directly, you can get some quality of life features like tab completion.
 If you haven't yet installed Bazel, and would like to, you can add it to apt and install it on Ubuntu systems with the following commands [as described in the Bazel documentation](https://bazel.build/install/ubuntu):
 
 ```console
@@ -100,118 +52,264 @@
 
 or by following [instructions for your system](https://bazel.build/install).
 
-### Invoking Bazel
+## Building Software with Bazel
 
-You can then build and run all the tests all the Bazel rules for OpenTitan within the workspace with the command:
-
+Running
 ```console
-bazel test //...
+bazel build //sw/...
+```
+will build all software in our repository.
+
+In general, you can build any software target (and all of it's dependencies) using the following syntax:
+```console
+bazel build @<repository>//<package>:<target>
+```
+Since most targets are within the main Bazel repository (`lowrisc_opentitan`), you can often drop the "`@<repository>`" prefix.
+For example, to build the boot ROM we use for testing (also referred to as the _test ROM_), you can use
+```console
+bazel build //sw/device/lib/testing/test_rom:test_rom
+```
+Additionally, some Bazel syntactic sugar enables dropping the target name when the target name matches the last subcomponent of the package name.
+For example, the following is equivalent to the above
+```console
+bazel build //sw/device/lib/testing/test_rom
 ```
 
-This is likely to include unhealthy or slow tests and build targets so you're likely to want to run more specific builds and tests.
-This invocation instructs Bazel to build everything it needs to run all the tests in the workspace.
+For more information on Bazel repositories, packages, and targets, please refer to the Bazel [documentation](https://bazel.build/concepts/build-ref).
 
-Specific targets can be built with:
+## Running Tests with Bazel
 
+In addition to building software, Bazel is also used to build and run tests.
+There are two categories of OpenTitan tests Bazel can build and run:
+1. on-host, and
+1. on-device.
+
+On-host tests are compiled and run on the host machine, while on-device tests are compiled and run on (simulated/emulated) OpenTitan hardware.
+
+Examples of on-host tests are:
+* unit tests for device software, such as [DIF]({{< relref "/sw/device/lib/dif/README.md" >}}) and [mask ROM]({{< relref "/sw/device/silicon_creator/mask_rom/docs/" >}}) unit tests.
+* any test for host software, such as `opentitan{lib,tool}`.
+
+Examples of on-device tests are:
+* [chip-level tests]({{< relref "/sw/device/tests/index.md" >}}).
+* [mask ROM functional tests]({{< relref "/sw/device/silicon_creator/mask_rom/docs/" >}})
+
+The remainder of this document will focus on building and running **on-host** tests with Bazel.
+To learn about running **on-device** tests with Bazel, please continue back to the main [Getting Started]({{< relref "getting_started" >}}) instructions, and proceed with the [Verilator]({{< relref "setup_verilator" >}}) and/or [FPGA]({{< relref "setup_fpga" >}}) setup instructions.
+
+### Running on-host DIF Tests
+
+The Device Interface Function or [DIF]({{< relref "/sw/device/lib/dif/README.md" >}}) libraries contain unit tests that run on the host and are built and run with Bazel.
+As shown below, you may use Bazel to query which tests are available, build and run all tests, or build and run only one test.
+
+#### Querying which tests are available
 ```console
-bazel build //<path to build file>:<name of target>
+bazel query 'tests(//sw/device/lib/dif:all)'
 ```
 
-### Structure of our Bazel workspace
+#### Building and running **all** tests
+```console
+bazel test //sw/device/lib/dif:all
+```
 
-The rules for Bazel are described in a language called starlark, which looks a lot like Python.
+#### Building and running a **single** test
+For example, building and testing the UART DIF library's unit tests:
+```console
+bazel test //sw/device/lib/dif:uart_unittest
+```
 
-The OpenTitan directory is defined as a Bazel workspace by the `//WORKSPACE` file.
+### Running on-host Mask ROM Tests
+
+Similar to the DIF libraries, you can query, build, and run all the [mask ROM]({{< relref "/sw/device/silicon_creator/mask_rom/docs/" >}}) unit tests (which also run on the host) with Bazel.
+
+#### Querying which (on-host) tests are available
+Note, the mask ROM has both on-host and on-device tests.
+This query filters tests by their kind, i.e., only on-host tests.
+```console
+bazel query 'kind(cc_.*, tests(//sw/device/silicon_creator/lib/...))'
+```
+
+#### Building and running **all** (on-host) tests
+```console
+bazel test --test_tag_filters=-cw310,-dv,-verilator //sw/device/silicon_creator/lib/...
+```
+
+#### Building and running a **single** (on-host) test
+For example, building and testing the mask ROM UART driver unit tests:
+```console
+bazel test //sw/device/silicon_creator/lib/drivers:uart_unittest
+```
+
+## OpenTitan Bazel Workspace
+
+The rules for Bazel are described in a language called Starlark, which looks a lot like Python.
+
+The `$REPO_TOP` directory is defined as a Bazel workspace by the `//WORKSPACE` file.
 `BUILD` files provide the information Bazel needs to build the targets in a directory.
 `BUILD` files also manage any subdirectories that don't have their own `BUILD` files.
 
-OpenTitan uses .bzl files to specify rules to build artifacts that require specific attention like target specific test rules and project specific binaries.
+OpenTitan uses .bzl files to specify custom rules to build artifacts that require specific attention like on-device test rules and project specific binaries.
 
-#### WORKSPACE file
+### WORKSPACE file
 
 The `WORKSPACE` file controls external dependencies such that builds can be made reproducible and hermetic.
-Bazel loads specific commits of external git repositories.
+Bazel loads specific external dependencies, such as various language toolchains.
 It uses them to build OpenTitan targets (like it does with bazel\_embedded) or to satisfy dependencies (as it does with abseil).
-To produce increasingly stable releases the `WORKSPACE` file will fix a number of these references to specific commits and not the default branch head.
-Dependencies will be added to the workspace so builds and tests are less sensitive to external updates and have a simpler getting started flow.
+To produce increasingly stable releases the external dependencies loaded in `WORKSPACE` file attempts to fix a all external `http_archive`s to a specific SHA.
+As we add more dependencies to the workspace, builds and tests will become less sensitive to external updates, and we will vastly simplify the [Getting Started]({{< relref "getting_started" >}}) instructions.
 
-#### BUILD files
+### BUILD files
 
-Throughout the OpenTitan repo, `BUILD` files describe targets and dependencies in the same directory and subdirectories that lack their own `BUILD` files.
-`BUILD` files are mostly hand-written; to suit the request that hand-written files not be included in autogen directories, there are large `BUILD` files that describe how to build and depend on auto-generated files in autogen subdirectories.
+Throughout the OpenTitan repository, `BUILD` files describe targets and dependencies in the same directory (and subdirectories that lack their own `BUILD` files).
+`BUILD` files are mostly hand-written.
+To maintain the invariant that hand-written files not be included in autogen directories, there are `BUILD` files that describe how to build and depend on auto-generated files in autogen subdirectories.
+
+## Linting Software
+
+There are several Bazel rules that enable running quality checks and fixers on code.
+The subsections below describe how to use them.
+All of the tools described below are run in CI on every pull request, so it is best to run them before committing code.
+
+### Linting C/C++ Code
+The OpenTitan supported linter for C/C++ files is `clang-format`.
+It can be run with Bazel as shown below.
+
+Run the following to check if all C/C++ code as been formatted correctly:
+```console
+bazel run //:clang_format_check
+```
+and run the following to fix it, if it is not formatted correctly.
+```console
+bazel run //:clang_format_fix
+```
 
 ### Linting Starlark
 
-The Linter for Bazel files is called "buildifier" and can be built and run with Bazel by running:
+The OpenTitan supported linter for Bazel files is `buildifier`.
+It can be run with Bazel as shown below.
 
+Run the following to check if all `WORKSPACE`, `BUILD`, and `.bzl` files have been formatted correctly:
+```console
+bazel run //:buildifier_check
+```
+and run the following to fix them, if they are not formatted correctly.
 ```console
 bazel run //:buildifier_fix
 ```
 
-Which will run the linter on all of the WORKSPACE, BUILD and .bzl files in the workspace.
-It's fairly strict so the best way to keep git logs and blame reports clean is to run it before committing, and ask the same during code review.
+### Checking License Headers
 
-### Test Execution
-
-Bazel will execute tests when invoked with `bazel test <label-expression>`.
-The test rules have various tags applied to them to allow for filtering of tests during dependency analysis.
-Our tests are typically tagged with verilator, cw310, or manual.
-The `--test_tag_filters` switch can be used to filter out tests that you either cannot or do not want to execute.
-
-For example, if you do not have a CW310 FPGA board, you cannot execute the tests meant to execute on that board.
-You can instruct Bazel to not execute those tests:
-
+Lastly, the OpenTitan supported linter for checking that every source code file contains a license header may be run with:
 ```console
-bazel test --test_tag_filters=-cw310 //...
+bazel run //:license_check
 ```
 
-Rather than manually adding that command line switch to every invocation of bazel, you can add it to your `$HOME/.bazelrc` file:
+## Miscellaneous
 
-```
-test --test_tag_filters=-cw310
-```
+### Bazel-built Artifacts
 
-### Excluding Verilator
+As decribed in the [OpenTitan Software]({{< relref "sw/" >}}) documentation, there are three categories of OpenTitan software, all of which are built with Bazel. These include:
+1. _device_ software,
+1. _OTBN_ software,
+1. _host_ software,
 
-Verilator is slow to build and Verilator tests are slow to run.
-Many directories have Verilator targets that will cause them to build Verilator when you build all targets with the wildcard `...`.
-Tagging allows us to exclude Verilator tests, but doesn't exclude building Verilator for targets that depend on it.
-To exclude all targets that depend on Verilator you should use a target pattern that can be generated with Bazel queries, and sed and then passed with xargs:
+Bazel produces various artifacts depending on the category of software that is built.
 
-```console
-bazel query "rdeps(//..., //hw:verilator)" \
-| sed -e 's/^/-/' \
-| xargs bazel test -- //...
-```
+#### Device Artifacts
 
-The Bazel query lists all the dependencies in the workspace that depend on Verilator.
-These are inverted for the target pattern by prepending a dash and then the test is invoked by xargs on a pattern which starts with the whole workspace and then excludes each of the targets that depend on Verilator.
+Device software is built and run on OpenTitan hardware.
+There are three OpenTitan "devices" for simulating/emulating OpenTitan hardware:
+1. DV simulation (i.e., RTL simulation with commercial simulators),
+1. Verilator simulation (i.e., RTL simulation with the open source Verilator simulator),
+1. FPGA.
 
-### Caching on disk
+Different software artifacts are built depending on the OpenTitan device above.
+Specifically, building an executable `<target>` destined to run on the OpenTitan device `<device>` will output the following files under `bazel-out/`:
+* `<target>_<device>`: the linked program, in ELF format.
+* `<target>_<device>.bin`: the linked program, as a plain binary with ELF debug information removed.
+* `<target>_<device>.elf.s`: the disassembled program with inline source code.
+* `<target>_<device>.*.vmem`: a Verilog memory file which can be read by `$readmemh()` in Verilog code.
 
-By default Bazel caches in memory and will evict Verilator binaries often.
-If you're regularly running tests that depend on Verilator and have a few GB of disk space available use the `--disk_cache=<filename>` to specify a cache:
+Note, `<device>` will be in {`sim_dv`, `sim_verilator`, `fpga_cw310`}, and `<target>` will end in the suffix "`_prog`" for executable images destined for flash.
 
-```console
-bazel build //... --disk_cache=~/bazel_cache
-```
+#### OTBN Artifacts
 
-Alternatively add the following to `$HOME/.bazelrc`:
+TODO: add documentation on OTBN artifacts.
 
-```
-build --disk_cache=~/bazel_cache
-```
+#### Host Artifacts
 
-## Disassembling device code
+Host software is built and run on the host hardware (e.g., an x64 Linux machine).
+A final linked program in the ELF format is all that is produced for host software builds.
+Note, like the device ELF, the file will not have an extension.
+
+### Disassembling Device Code
 
 A disassembly of all executable sections is produced by the build system by default.
-It can be found by looking for files with the `.dis` extension next to the corresponding ELF file.
+It can be found by looking for files with the `.elf.s` extension next to the corresponding ELF file.
 
 To get a different type of disassembly, e.g. one which includes data sections in addition to executable sections, objdump can be called manually.
 For example the following command shows how to disassemble all sections of the UART DIF smoke test interleaved with the actual source code:
 
 ```console
-riscv32-unknown-elf-objdump --disassemble-all --headers --line-numbers --source build-bin/sw/device/tests/uart_smoketest_sim_verilator.elf
+riscv32-unknown-elf-objdump --disassemble-all --headers --line-numbers --source \
+  $(find -L bazel-out/ -type f -name "uart_smoketest_prog_sim_verilator")
 ```
 
 Refer to the output of `riscv32-unknown-elf-objdump --help` for a full list of options.
+
+### Locating Bazel-built Artifacts
+
+Bazel built artifacts can be located in the symlinked `bazel-out/` directory that gets automatically created on invocations of Bazel.
+To locate build artifacts, you may use the `find` utility.
+For example, after building the UART smoke test device software with
+```console
+bazel build //sw/device/tests:uart_smoketest_sim_verilator
+```
+to locate the `.bin` file use
+```console
+find -L bazel-bin/ -type f -name "uart_smoketest_prog_sim_verilator.bin"
+```
+
+### Troubleshooting Builds
+
+If you encounter an unexplained error building or running any `bazel` commands, you can issue a subsequent `bazel clean` command to erase any existing building directories to yield a clean build.
+Specifically, according to the Bazel [documentation](https://docs.bazel.build/versions/main/user-manual.html#clean), issuing a
+```console
+bazel clean
+```
+deletes all the output build directories, while running a
+```console
+bazel clean --expunge
+```
+will wipe all disk and memory traces (i.e., any cached intermediate files) produced by Bazel.
+The latter sledgehammer is only intended to be used as a last resort when the existing configuration is seriously broken.
+
+### Disk Cache
+
+Bazel can use a directory on the file system as a remote cache.
+This is useful for sharing build artifacts across multiple [`git` worktrees](https://git-scm.com/docs/git-worktree) or multiple workspaces of the same project, such as multiple checkouts.
+
+Use the `--disk_cache=<filename>` to specify a cache directory.
+For example, running
+```console
+bazel build //... --disk_cache=~/bazel_cache
+```
+will cache all built artifacts.
+
+Alternatively add the following to `$HOME/.bazelrc` to avoid having automatically use the disk cache on every Bazel invocation.
+```
+build --disk_cache=~/bazel_cache
+```
+
+For more documentation on Bazel disk caches see the [official documentation](https://docs.bazel.build/versions/main/remote-caching.html#disk-cache).
+
+### Excluding Verilator Simulation Binary Builds
+
+Many device software targets depend on the Verilator simulation binary,
+The Verilator simulation binary is slow to build.
+To avoid building it, use the
+`--define DISABLE_VERILATOR_BUILD=true` build option.
+For example, to build the UART smoke test artifacts but not the Verilator simulation binary run
+```console
+bazel build --define DISABLE_VERILATOR_BUILD=true //sw/device/tests:uart_smoketest
+```
diff --git a/doc/getting_started/setup_verilator.md b/doc/getting_started/setup_verilator.md
index 225e140..64e776c 100644
--- a/doc/getting_started/setup_verilator.md
+++ b/doc/getting_started/setup_verilator.md
@@ -11,8 +11,6 @@
 Verilator is a cycle-accurate simulation tool.
 It translates synthesizable Verilog code into a simulation program in C++, which is then compiled and executed.
 
-<!-- TODO: Switch all calls to fusesoc and the Verilated system to refer to Meson, once it supports fusesoc. -->
-
 ### Install Verilator
 
 Even though Verilator is packaged for most Linux distributions these versions tend to be too old to be usable.
@@ -21,16 +19,16 @@
 Fetch, build and install Verilator itself (this should be done outside the `$REPO_TOP` directory).
 
 ```console
-$ export VERILATOR_VERSION={{< tool_version "verilator" >}}
+export VERILATOR_VERSION={{< tool_version "verilator" >}}
 
-$ git clone https://github.com/verilator/verilator.git
-$ cd verilator
-$ git checkout v$VERILATOR_VERSION
+git clone https://github.com/verilator/verilator.git
+cd verilator
+git checkout v$VERILATOR_VERSION
 
-$ autoconf
-$ ./configure --prefix=/tools/verilator/$VERILATOR_VERSION
-$ make
-$ make install
+autoconf
+./configure --prefix=/tools/verilator/$VERILATOR_VERSION
+make
+make install
 ```
 
 After installation you need to add `/tools/verilator/$VERILATOR_VERSION/bin` to your `PATH` environment variable.
@@ -49,110 +47,98 @@
 
 If you need to install to a different location than `/tools/verilator/...`, you can pass a different directory to `./configure --prefix` above and add `your/install/location/bin` to `PATH` instead.
 
-## Simulating a design with Verilator
+## Running Software on a Verilator Simulation with Bazel
 
 First the RTL must be built into a simulator binary.
 This is done by running fusesoc, which collects up RTL code and passes it to Verilator to generate and then compile a C++ model.
-The fusesoc command line arguments are reasonably complicated so we have a wrapper script:
-
-```console
-$ cd $REPO_TOP
-$ ci/scripts/build-chip-verilator.sh earlgrey
-```
-
-Then we need to build software to run on the simulated system.
-If you followed the [building software guide]({{< relref "build_sw" >}}), you've done this step already.
-
-```console
-$ cd $REPO_TOP
-$ ./meson_init.sh
-$ ninja -C build-out all
-```
-
-The above command also builds the OTP image that contains the root secrets and life cycle state.
-By default, the life cycle state will be moved into DEV, which enables debugging features such as the JTAG interface for the main processor.
-
-Now the simulation can be run.
-
-```console
-$ cd $REPO_TOP
-$ build-bin/hw/top_earlgrey/Vchip_earlgrey_verilator \
-  --meminit=rom,build-bin/sw/device/lib/testing/test_rom/test_rom_sim_verilator.scr.39.vmem \
-  --meminit=flash,build-bin/sw/device/examples/hello_world/hello_world_sim_verilator.64.scr.vmem \
-  --meminit=otp,build-bin/sw/device/otp_img/otp_img_sim_verilator.vmem
-```
-
-To stop the simulation press CTRL-c.
-
-The programs listed after `--meminit` are loaded into the system's specified memory and execution is started immediately.
-There are 4 memory types: ROM, Flash, OTP, and SRAM.
+Next software must be built to run on the simulated hardware.
+There are 4 memory types on OpenTitan hardware: ROM, Flash, OTP, and SRAM.
+Software images need to be provided for ROM, Flash, and OTP (SRAM is populated at runtime).
 By default, the system will first execute out of ROM and then jump to Flash.
-Memory images need to be provided for ROM, Flash, and OTP (SRAM is populated at runtime).
+The OTP image does not contain executable code, rather it contains root secrets, runtime configuration data, and life cycle state.
+(By default, the life cycle state is set to RMA, which enables debugging features such as the JTAG interface for the main processor.)
+Lastly, the Verilator simulation binary must be run with the correct arguments.
 
-If you want to run a program other than `hello_world`, you'll change the second (flash) `--meminit` argument to point to a different `64.scr.vmem` file under `build-bin`.
-For the most part, the structure under `build-bin` follows the structure in the repository, and executables intended for Verilator are suffixed with `sim_verilator`.
-For example, the `build-bin/sw/device/examples/hello_world/hello_world_sim_verilator.64.scr.vmem` file used above corresponds to the `sw/device/examples/hello_world/hello_world.c` source file.
+Thankfully, Bazel (and `opentitantool`) simplify this process by providing a single interface for performing all of the above steps.
+Moreover, Bazel automatically connects to the simulated UART (via `opentitantool`) to print the test output in real time.
 
-All executed instructions in the loaded software are logged to the file `trace_core_00000000.log`.
-The columns in this file are tab separated; change the tab width in your editor if the columns don't appear clearly, or open the file in a spreadsheet application.
-
-## Interact with the simulated UART
-
-When starting the simulation you should see a message like:
+For example, to run the UART smoke test on Verilator simulated hardware, and see the output in real time, use
 ```console
-UART: Created /dev/pts/11 for uart0. Connect to it with any terminal program, e.g.
-$ screen /dev/pts/11
+cd $REPO_TOP
+bazel test --test_tag_filters=verilator --test_output=streamed //sw/device/tests:uart_smoketest
+```
+or
+```console
+cd $REPO_TOP
+bazel test --test_output=streamed //sw/device/tests:uart_smoketest_sim_verilator
 ```
 
-Use any terminal program, e.g. `screen` to connect to the simulation.
-If you only want to see the program output you can use `cat` instead.
-
+You should expect to see something like:
 ```console
-$ # to only see the program output
-$ cat /dev/pts/11
+Invoking: sw/host/opentitantool/opentitantool --rcfile= --logging=info --interface=verilator --conf=sw/host/opentitantool/config/opentitan_verilator.json --verilator-bin=hw/build.verilator_real/sim-verilator/Vchip_sim_tb --verilator-rom=sw/device/lib/testing/test_rom/test_rom_sim_verilator.scr.39.vmem --verilator-flash=sw/device/tests/uart_smoketest_prog_sim_verilator.64.scr.vmem --verilator-otp=hw/ip/otp_ctrl/data/img_rma.vmem console --exit-failure=(FAIL|FAULT).*\n --exit-success=PASS.*\n --timeout=3600s
+[2022-06-09T08:08:16Z INFO  opentitanlib::transport::verilator::subprocess] Spawning verilator: "hw/build.verilator_real/sim-verilator/Vchip_sim_tb" ["--meminit=rom,sw/device/lib/testing/test_rom/test_rom_sim_verilator.scr.39.vmem", "--meminit=flash,sw/device/tests/uart_smoketest_prog_sim_verilator.64.scr.vmem", "--meminit=otp,hw/ip/otp_ctrl/data/img_rma.vmem"]
+[2022-06-09T08:08:16Z INFO  opentitanlib::transport::verilator::stdout] Simulation of OpenTitan Earl Grey
+[2022-06-09T08:08:16Z INFO  opentitanlib::transport::verilator::stdout] =================================
+[2022-06-09T08:08:16Z INFO  opentitanlib::transport::verilator::stdout]
+[2022-06-09T08:08:16Z INFO  opentitanlib::transport::verilator::stdout]
+[2022-06-09T08:08:16Z INFO  opentitanlib::transport::verilator::stdout] Tracing can be toggled by sending SIGUSR1 to this process:
+[2022-06-09T08:08:16Z INFO  opentitanlib::transport::verilator::stdout] $ kill -USR1 3422749
+[2022-06-09T08:08:16Z INFO  opentitanlib::transport::verilator::stdout]
+[2022-06-09T08:08:16Z INFO  opentitanlib::transport::verilator::stdout]
+[2022-06-09T08:08:16Z INFO  opentitanlib::transport::verilator::stdout] GPIO: FIFO pipes created at $HOME/.cache/bazel/_bazel_ttrippel/3d92022c091a734228e22679f3ac7c7f/execroot/lowrisc_opentitan/bazel-out/k8-fastbuild/bin/sw/device/tests/uart_smoketest_sim_verilator.runfiles/lowrisc_opentitan/gpio0-read (read) and $HOME/.cache/bazel/_bazel_ttrippel/3d92022c091a734228e22679f3ac7c7f/execroot/lowrisc_opentitan/bazel-out/k8-fastbuild/bin/sw/device/tests/uart_smoketest_sim_verilator.runfiles/lowrisc_opentitan/gpio0-write (write) for 32-bit wide GPIO.
+[2022-06-09T08:08:16Z INFO  opentitanlib::transport::verilator::stdout] GPIO: To measure the values of the pins as driven by the device, run
+[2022-06-09T08:08:16Z INFO  opentitanlib::transport::verilator::stdout] $ cat $HOME/.cache/bazel/_bazel_ttrippel/3d92022c091a734228e22679f3ac7c7f/execroot/lowrisc_opentitan/bazel-out/k8-fastbuild/bin/sw/device/tests/uart_smoketest_sim_verilator.runfiles/lowrisc_opentitan/gpio0-read  # '0' low, '1' high, 'X' floating
+[2022-06-09T08:08:16Z INFO  opentitanlib::transport::verilator::stdout] GPIO: To drive the pins, run a command like
+[2022-06-09T08:08:16Z INFO  opentitanlib::transport::verilator::stdout] $ echo 'h09 l31' > $HOME/.cache/bazel/_bazel_ttrippel/3d92022c091a734228e22679f3ac7c7f/execroot/lowrisc_opentitan/bazel-out/k8-fastbuild/bin/sw/device/tests/uart_smoketest_sim_verilator.runfiles/lowrisc_opentitan/gpio0-write  # Pull the pin 9 high, and pin 31 low.
+[2022-06-09T08:08:16Z INFO  opentitanlib::transport::verilator::stdout]
+[2022-06-09T08:08:16Z INFO  opentitanlib::transport::verilator::stdout] SPI: Created /dev/pts/9 for spi0. Connect to it with any terminal program, e.g.
+[2022-06-09T08:08:16Z INFO  opentitanlib::transport::verilator::stdout] $ screen /dev/pts/9
+[2022-06-09T08:08:16Z INFO  opentitanlib::transport::verilator::stdout] NOTE: a SPI transaction is run for every 4 characters entered.
+[2022-06-09T08:08:16Z INFO  opentitanlib::transport::verilator::stdout] SPI: Monitor output file created at $HOME/.cache/bazel/_bazel_ttrippel/3d92022c091a734228e22679f3ac7c7f/execroot/lowrisc_opentitan/bazel-out/k8-fastbuild/bin/sw/device/tests/uart_smoketest_sim_verilator.runfiles/lowrisc_opentitan/spi0.log. Works well with tail:
+[2022-06-09T08:08:16Z INFO  opentitanlib::transport::verilator::stdout] $ tail -f $HOME/.cache/bazel/_bazel_ttrippel/3d92022c091a734228e22679f3ac7c7f/execroot/lowrisc_opentitan/bazel-out/k8-fastbuild/bin/sw/device/tests/uart_smoketest_sim_verilator.runfiles/lowrisc_opentitan/spi0.log
+[2022-06-09T08:08:16Z INFO  opentitanlib::transport::verilator::stdout]
+[2022-06-09T08:08:16Z INFO  opentitanlib::transport::verilator::stdout] UART: Created /dev/pts/10 for uart0. Connect to it with any terminal program, e.g.
+[2022-06-09T08:08:16Z INFO  opentitanlib::transport::verilator::stdout] $ screen /dev/pts/10
+[2022-06-09T08:08:16Z INFO  opentitanlib::transport::verilator::stdout] UART: Additionally writing all UART output to 'uart0.log'.
+[2022-06-09T08:08:16Z INFO  opentitanlib::transport::verilator::stdout]
+[2022-06-09T08:08:16Z INFO  opentitanlib::transport::verilator::stdout] USB: Monitor output file created at $HOME/.cache/bazel/_bazel_ttrippel/3d92022c091a734228e22679f3ac7c7f/execroot/lowrisc_opentitan/bazel-out/k8-fastbuild/bin/sw/device/tests/uart_smoketest_sim_verilator.runfiles/lowrisc_opentitan/usb0.log. Works well with tail:
+[2022-06-09T08:08:16Z INFO  opentitanlib::transport::verilator::stdout] $ tail -f $HOME/.cache/bazel/_bazel_ttrippel/3d92022c091a734228e22679f3ac7c7f/execroot/lowrisc_opentitan/bazel-out/k8-fastbuild/bin/sw/device/tests/uart_smoketest_sim_verilator.runfiles/lowrisc_opentitan/usb0.log
+[2022-06-09T08:08:16Z INFO  opentitanlib::transport::verilator::stdout]
+[2022-06-09T08:08:16Z INFO  opentitanlib::transport::verilator::stdout] JTAG: Virtual JTAG interface dmi0 is listening on port 44853. Use
+[2022-06-09T08:08:16Z INFO  opentitanlib::transport::verilator::stdout] OpenOCD and the following configuration to connect:
+[2022-06-09T08:08:16Z INFO  opentitanlib::transport::verilator::stdout]   interface remote_bitbang
+[2022-06-09T08:08:16Z INFO  opentitanlib::transport::verilator::stdout]   remote_bitbang_host localhost
+[2022-06-09T08:08:16Z INFO  opentitanlib::transport::verilator::stdout]   remote_bitbang_port 44853
+[2022-06-09T08:08:16Z INFO  opentitanlib::transport::verilator::stdout]
+[2022-06-09T08:08:16Z INFO  opentitanlib::transport::verilator::stdout] Simulation running, end by pressing CTRL-c.
+[2022-06-09T08:08:16Z INFO  opentitanlib::transport::verilator::stdout]
+[2022-06-09T08:08:17Z INFO  opentitanlib::transport::verilator::transport] Verilator started with the following interaces:
+[2022-06-09T08:08:17Z INFO  opentitanlib::transport::verilator::transport] gpio_read = $HOME/.cache/bazel/_bazel_ttrippel/3d92022c091a734228e22679f3ac7c7f/execroot/lowrisc_opentitan/bazel-out/k8-fastbuild/bin/sw/device/tests/uart_smoketest_sim_verilator.runfiles/lowrisc_opentitan/gpio0-read
+[2022-06-09T08:08:17Z INFO  opentitanlib::transport::verilator::transport] gpio_write = $HOME/.cache/bazel/_bazel_ttrippel/3d92022c091a734228e22679f3ac7c7f/execroot/lowrisc_opentitan/bazel-out/k8-fastbuild/bin/sw/device/tests/uart_smoketest_sim_verilator.runfiles/lowrisc_opentitan/gpio0-write
+[2022-06-09T08:08:17Z INFO  opentitanlib::transport::verilator::transport] uart = /dev/pts/10
+[2022-06-09T08:08:17Z INFO  opentitanlib::transport::verilator::transport] spi = /dev/pts/9
+Starting interactive console
+[CTRL+C] to exit.
 
-$ # to interact with the simulation
-$ screen /dev/pts/11
-```
+I00000 test_rom.c:81] Version:    earlgrey_silver_release_v5-5775-gefa09d3b8
+Build Date: 2022-06-09, 00:12:35
 
-Note that `screen` will only show output that has been generated after `screen` starts, whilst `cat` will show output that was produced before `cat` started.
+I00001 test_rom.c:118] Test ROM complete, jumping to flash!
+I00000 status.c:28] PASS!
 
-You can exit `screen` (in the default configuration) by pressing `CTRL-a k` and confirm with `y`.
 
-If everything is working correctly you should expect to see text like the following from the virtual UART (replacing `/dev/pts/11` with the reported device):
-
-```console
-$ cat /dev/pts/11
-I00000 test_rom.c:35] Version:    opentitan-snapshot-20191101-1-1182-g2aedf641
-Build Date: 2020-05-13, 15:04:09
-
-I00001 test_rom.c:44] Boot ROM initialisation has completed, jump into flash!
-I00000 hello_world.c:30] Hello World!
-I00001 hello_world.c:31] Built at: May 13 2020, 15:27:31
-I00002 demos.c:17] Watch the LEDs!
-I00003 hello_world.c:44] Try out the switches on the board
-I00004 hello_world.c:45] or type anything into the console window.
-I00005 hello_world.c:46] The LEDs show the ASCII code of the last character.
-```
-
-Instead of interacting with the UART through a pseudo-terminal, the UART output can be written to a log file, or to STDOUT.
-This is done by passing the `UARTDPI_LOG_uart0` plus argument ("plusarg") to the verilated simulation at runtime.
-To write all UART output to STDOUT, pass `+UARTDPI_LOG_uart0=-` to the simulation.
-To write all UART output to a file called `your-log-file.log`, pass `+UARTDPI_LOG_uart0=your-log-file.log`.
-
-A full command-line invocation of the simulation could then look like that:
-```console
-$ cd $REPO_TOP
-$ build/lowrisc_dv_chip_verilator_sim_0.1/sim-verilator/Vchip_sim_tb \
-  --meminit=rom,build-bin/sw/device/lib/testing/test_rom/test_rom_sim_verilator.scr.39.vmem \
-  --meminit=flash,build-bin/sw/device/examples/hello_world/hello_world_sim_verilator.64.scr.vmem \
-  --meminit=otp,build-bin/sw/device/otp_img/otp_img_sim_verilator.vmem
-  +UARTDPI_LOG_uart0=-
+Exiting interactive console.
+[2022-06-09T08:09:38Z INFO  opentitantool::command::console] ExitSuccess("PASS!\r\n")
 ```
 
 **For most use cases, interacting with the UART is all you will need and you can stop here.**
 However, if you want to interact with the simulation in additional ways, there are more options listed below.
 
+## Execution Log
+
+All executed instructions in the loaded software into Verilator simulations are logged to the file `trace_core_00000000.log`.
+The columns in this file are tab separated; change the tab width in your editor if the columns don't appear clearly, or open the file in a spreadsheet application.
+
 ## Interact with GPIO (optional)
 
 The simulation includes a DPI module to map general-purpose I/O (GPIO) pins to two POSIX FIFO files: one for input, and one for output.
@@ -170,7 +156,6 @@
 $ echo 'h09 l31' > gpio0-write  # Pull the pin 9 high, and pin 31 low.
 ```
 
-
 ## Connect with OpenOCD to the JTAG port and use GDB (optional)
 
 The simulation includes a "virtual JTAG" port to which OpenOCD can connect using its `remote_bitbang` driver.
@@ -178,18 +163,25 @@
 
 See the [OpenOCD install instructions]({{< relref "install_openocd.md" >}}) for guidance on installing OpenOCD.
 
-Run the simulation, then connect with OpenOCD using the following command.
-
+Run the simulation with Bazel, making sure to build the device software with debug symbols using
 ```console
-$ cd $REPO_TOP
-$ /tools/openocd/bin/openocd -s util/openocd -f board/lowrisc-earlgrey-verilator.cfg
+cd $REPO_TOP
+bazel test --copt=-g --test_output=streamed //sw/device/tests:uart_smoketest_sim_verilator
 ```
 
-To connect GDB use the following command (noting it needs to be altered to point to the sw binary in use).
+Then, connect with OpenOCD using the following command.
 
 ```console
-$ riscv32-unknown-elf-gdb -ex "target extended-remote :3333" -ex "info reg" \
-  build-bin/sw/device/examples/hello_world/hello_world_sim_verilator.elf
+cd $REPO_TOP
+/tools/openocd/bin/openocd -s util/openocd -f board/lowrisc-earlgrey-verilator.cfg
+```
+
+Lastly, connect GDB using the following command (noting it needs to be altered to point to the sw binary in use).
+
+```console
+cd $REPO_TOP
+riscv32-unknown-elf-gdb -ex "target extended-remote :3333" -ex "info reg" \
+  $(find -L bazel-out/ -type f -name "uart_smoketest_sim_verilator | head -n 1")
 ```
 
 ## SPI device test interface (optional)
@@ -208,12 +200,12 @@
 Use any terminal program, e.g. `screen` or `microcom` to connect to the simulation.
 
 ```console
-$ screen /dev/pts/4
+screen /dev/pts/4
 ```
 
 Microcom seems less likely to send unexpected control codes when starting:
 ```console
-$ microcom -p /dev/pts/4
+microcom -p /dev/pts/4
 ```
 
 The terminal will accept (but not echo) characters.
@@ -228,15 +220,17 @@
 
 ## Generating waveforms (optional)
 
+TODO(lowRISC/opentitan[#13042](https://github.com/lowRISC/opentitan/issues/13042)): the below does not work with the Bazel test running flow.
+
 With the `--trace` argument the simulation generates a FST signal trace which can be viewed with Gtkwave (only).
 Tracing slows down the simulation by roughly factor of 1000.
 
 ```console
-$ cd $REPO_TOP
-$ build/lowrisc_dv_chip_verilator_sim_0.1/sim-verilator/Vchip_sim_tb \
+cd $REPO_TOP
+build/lowrisc_dv_chip_verilator_sim_0.1/sim-verilator/Vchip_sim_tb \
   --meminit=rom,build-bin/sw/device/lib/testing/test_rom/test_rom_sim_verilator.scr.39.vmem \
   --meminit=flash,build-bin/sw/device/examples/hello_world/hello_world_sim_verilator.64.scr.vmem \
   --meminit=otp,build-bin/sw/device/otp_img/otp_img_sim_verilator.vmem \
   --trace
-$ gtkwave sim.fst
+gtkwave sim.fst
 ```
diff --git a/rules/fusesoc.bzl b/rules/fusesoc.bzl
index 71f6f51..bed465c 100644
--- a/rules/fusesoc.bzl
+++ b/rules/fusesoc.bzl
@@ -36,6 +36,8 @@
         verilator_options = ctx.attr.verilator_options[BuildSettingInfo].value
         flags.append("--verilator_options={}".format(" ".join(verilator_options)))
 
+    # Note: the `fileset_top` flag used above is specific to the OpenTitan
+    # project to select the correct RTL fileset.
     ctx.actions.run(
         mnemonic = "FuseSoC",
         outputs = outputs,
diff --git a/sw/README.md b/sw/README.md
index 2a0758c..b1af82e 100644
--- a/sw/README.md
+++ b/sw/README.md
@@ -2,23 +2,13 @@
 title: "OpenTitan Software Build Instructions"
 ---
 
-OpenTitan software is built using [Meson](https://mesonbuild.com), although OpenTitan's project structure is sufficiently idiosyncratic that we use a custom workflow.
+All OpenTitan software is built with [Bazel](https://bazel.build/).
 
-For example, to build the OpenTitan executable located at `sw/device/examples/hello_world` for FPGA, run the following commands:
+For example, to build and run the OpenTitan UART smoke test located in `sw/device/tests/` for Verilator, run
 
 ```console
-$ cd "$REPO_TOP"
-$ ./meson_init.sh
-$ ninja -C build-out sw/device/examples/hello_world/hello_world_export_fpga_cw310
+cd "$REPO_TOP"
+bazel test --test_output=streamed //sw/device/tests:uart_smoketest_sim_verilator
 ```
 
-The resulting binaries will be located at `build-bin/sw/device/examples/hello_world`. For more information, check out [the Building Software guide]({{< relref "doc/getting_started/build_sw" >}}).
-
-The location of the RISC-V toolchain is /tools/riscv by default.
-If your toolchain is located elsewhere set the `TOOLCHAIN_PATH` to that path before running `meson_init.sh`
-
-```console
-$ cd "$REPO_TOP"
-$ export TOOLCHAIN_PATH=/path/to/toolchain
-$ ./meson_init.sh
-```
+The resulting binaries will be located under `bazel-out/`. For more information, check out [the Building Software guide]({{< relref "doc/getting_started/build_sw" >}}).
diff --git a/sw/device/benchmarks/coremark/README.md b/sw/device/benchmarks/coremark/README.md
index 9b8fb52..c70a84a 100644
--- a/sw/device/benchmarks/coremark/README.md
+++ b/sw/device/benchmarks/coremark/README.md
@@ -2,6 +2,8 @@
 title: "CoreMark Benchmark"
 ---
 
+TODO(lowRISC/opentitan[#12504](https://github.com/lowRISC/opentitan/issues/12504)): update instructions for Bazel builds.
+
 ## Building CoreMark
 
 To build CoreMark under meson:
diff --git a/sw/device/lib/_index.md b/sw/device/lib/_index.md
index a81bb5a..eec411d 100644
--- a/sw/device/lib/_index.md
+++ b/sw/device/lib/_index.md
@@ -16,9 +16,8 @@
     The Base Libraries are simple libraries that can be used by any other OpenTitan device software libraries.
 -   [`sw/device/lib/dif`]({{< relref "sw/device/lib/dif/README.md" >}})
     contains the [Device Interface Functions]({{< relref "doc/rm/device_interface_functions.md" >}}).
--   [`sw/device/lib/arch`]({{< relref "sw/device/lib/arch/README.md" >}})
-    contains the libraries to be used on specific device configurations (for
-    instance FPGA, Simulation, etc.).
+-   `sw/device/lib/arch` contains the libraries to be used on specific device
+    configurations (for instance FPGA, Simulation, etc.).
 -   [`sw/device/lib/runtime`]({{< relref "sw/device/lib/runtime/README.md" >}})
     contains general libraries for more advanced on-device functionality
     (including logging, printing, and interacting with the RISC-V core).
diff --git a/sw/device/lib/arch/README.md b/sw/device/lib/arch/README.md
deleted file mode 100644
index cca41d8..0000000
--- a/sw/device/lib/arch/README.md
+++ /dev/null
@@ -1,58 +0,0 @@
----
-title: "arch: Device-specific Symbols"
----
-
-This subtree provides the header `device.h`, which contains declarations for symbols that represent device-specific information, like the clock frequency.
-
-## Using `device.h`
-
-When a library needs to make use of device-specific information, it should only pull in the header itself, and not depend directly on any of the libraries in this directory.
-Instead, the symbols' definitions will be provided at link time by the executable's build rule, which should depend on exactly one of the libraries in this directory (failing to do so is Undefined Behavior).
-
-If an executable is designed to be device-independent (i.e., obtains all of its device-specific information from `device.h`), the following Meson template may be used to generate executable targets for most devices:
-```meson
-foreach device_name, device_lib : sw_lib_arch_core_devices
-  executable(
-    'my_binary_' + device_name,
-    sources: [...],
-    dependencies: [
-      ...,
-      device_lib,
-    ],
-  )
-
-  # ...
-endforeach
-```
-Note that this will not generate targets for some specialized devices, such as DV testbenches.
-
-## Adding a new device
-
-It is sometimes necessary to add a new device. The following considerations should be taken:
-- Make sure to add a new entry to `device_type_t`. Multiple variants of the same device (e.g., a DV testbench with different settings) may use the same `device_type_t`.
-- If your device is not "specialized" (i.e., not a DV testbench), it should be added to `sw_lib_arch_core_devices`.
-- Users should only ever link in exactly one target from this directory.
-  If multiple devices share a lot of the same symbol definitions, those can be factored into a separate `.c` file, but the targets should, ultimately, look like this:
-  ```meson
-  sw_lib_arch_my_dev1 = declare_dependency(
-    link_with: static_library(
-      'my_dev1',
-      sources: [
-        'device_my_dev_base.c'
-        'device_my_dev1.c'
-      ],
-    ),
-  )
-
-  sw_lib_arch_my_dev2 = declare_dependency(
-    link_with: static_library(
-      'my_dev2',
-      sources: [
-        'device_my_dev_base.c'
-        'device_my_dev2.c'
-      ],
-    ),
-  )
-  ```
-- Your device may be specialized and require symbols that are straight-up not meaningful on other devices.
-  These symbols should be specified in a separate header in this subtree, and targets that use that header should not be declared using the `sw_lib_arch_core_devices` shorthand above.